NewLife.XCode是一个有10多年历史的开源数据中间件,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
整个系列教程会大量结合示例代码和运行日志来进行深入分析,蕴含多年开发经验于其中。
开源地址:https://github.com/NewLifeX/X (求star, 620+)
回到目录
生成实体类
上一章《数据模型》讲到模型文件Model.xml和脚本Build.tt,(nuget安装NewLife.XCode后即可拥有)。
把Build.tt和Model.xml(可改名)放在同一个目录,在Build.tt上右键“运行自定义工具”,“显示所有文件”,即可看到生成的实体类文件。
**如果运行Build.tt出错,可能是因为找不到XCode.dll文件,可以先编译一次项目,让XCode.dll生成到项目输出目录即可
我们来试试以下模型(拷贝到Model.xml里面):
<?xml version="1.0" encoding="utf-8"?>
<Tables Version="9.9.6940.24706" NameSpace="NewLife.School.Entity" ConnName="School" BaseClass="Entity" Output="">
<Table Name="Class" TableName="xxx_class" Description="班级" DbType="SqlServer">
<Columns>
<Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
<Column Name="Name" ColumnName="xxx_Nameyyy" DataType="String" Master="True" Description="名称" />
<Column Name="CreateUser" DataType="String" Description="创建者" />
<Column Name="CreateUserID" DataType="Int32" Description="创建者" />
<Column Name="CreateTime" DataType="DateTime" Description="创建时间" />
<Column Name="CreateIP" DataType="String" Description="创建地址" />
<Column Name="UpdateUser" DataType="String" Description="更新者" />
<Column Name="UpdateUserID" DataType="Int32" Description="更新者" />
<Column Name="UpdateTime" DataType="DateTime" Description="更新时间" />
<Column Name="UpdateIP" DataType="String" Description="更新地址" />
<Column Name="Remark" DataType="String" Length="200" Description="备注" />
</Columns>
</Table>
<Table Name="Student" Description="学生" DbType="SqlServer">
<Columns>
<Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
<Column Name="ClassID" DataType="Int32" Description="班级" />
<Column Name="Name" DataType="String" Master="True" Description="名称" />
<Column Name="Sex" DataType="Int32" Description="性别" Type="XCode.Membership.SexKinds" />
<Column Name="Age" DataType="Int32" Description="年龄" />
<Column Name="Mobile" DataType="String" Description="手机" />
<Column Name="Address" DataType="String" Description="地址" />
<Column Name="CreateUserID" DataType="Int32" Description="创建者" />
<Column Name="CreateTime" DataType="DateTime" Description="创建时间" />
<Column Name="CreateIP" DataType="String" Description="创建地址" />
<Column Name="UpdateUserID" DataType="Int32" Description="更新者" />
<Column Name="UpdateTime" DataType="DateTime" Description="更新时间" />
<Column Name="UpdateIP" DataType="String" Description="更新地址" />
<Column Name="Remark" DataType="String" Length="200" Description="备注" />
</Columns>
<Indexes>
<Index Columns="ClassID" />
</Indexes>
</Table>
</Tables>
运行build.tt后
每个模型表,生成了四个实体类文件,选中它们并包含到项目中。
其中Biz常称之为业务类,多次build.tt生成不覆盖;
另一个称之为数据类,每次build.tt生成均覆盖;
这里采用了C#的分部类(partial)技术,一个类由两个或多个类文件组成。
数据类包含表名(类名)字段名(属性)等信息,修改模型文件后,每次生成都会覆盖文件。
业务类包含其它非表结构信息,供开发者填写代码,所以只有首次生成,而再次生成时不会覆盖。
数据类包括一个接口(如IStudent),以满足精简需要的场合。
数据类内部还有两个内嵌类_和__,可用于快速访问字段信息以及属性名。
回到目录
实体静态构造函数
XCode是充血模型,因此实体类除了各个代表着表结构信息的属性外,还会有大量用户代码在其中,并且继承泛型实体基类(如Entity<User>)。
一个常见的实体类构造函数如下:
static User()
{
// 累加字段
var df = Meta.Factory.AdditionalFields;
df.Add(__.Logins);
// 过滤器 UserModule、TimeModule、IPModule
Meta.Modules.Add<UserModule>();
Meta.Modules.Add<TimeModule>();
Meta.Modules.Add<IPModule>();
// 单对象缓存
var sc = Meta.SingleCache;
sc.FindSlaveKeyMethod = k => Find(__.Name, k);
sc.GetSlaveKeyMethod = e => e.Name;
}
这里首先介绍一个最重要的实体类内嵌类Meta,它位于Entity<TEntity>.Meta,记录着实体类的一切元数据,承载着实体类的一切高级功能!
Meta.Factory.AdditionalFields用于存放累加字段
一般更新语句 update user set Logins=123 where id=1,而把Logins字段设为累加字段后,将得到 update user set Logins=Logins+33 where id=1 ,特别适用于并发更新同一行记录的场合。
实体过滤器EntityModule,用于拦截实体类的添删改操作,内置最常用的3个过滤器UserModule/TimeModule/IPModule
上一章末尾推荐的8个常用字段还记得吗? CreateUser/CreateTime/CreateIP 等,所有CreateAbc将在Insert的时候拦截赋值,所有UpdateAbc将在Insert和Update的时候拦截赋值。
UserModule取当前登录用户,由ManageProvider驱动;
TimeModule取当前时间;
IPModule取当前访问IP,由ManageProvider.UserHost提供;
缓存配置
单对象缓存是一个字典缓存,默认以主键为key,实体对象为value。
单对象缓存支持第二个字典,如上,配置Name为第二字典的主键,实体对象为value。
实体基类
当然,实体类静态构造函数还可以用于其它用途,它将会在使用该实体类任意方法(包括成员方法和静态方法)之前执行。
有时候把一个系统模块放到一个独立子目录里面,独享一个“Abc.xml”模型文件,生成的实体类在目录里面,这个时候可以让它们继承一个相同的实体基类(如EntityBase)。
然后在实体基类EntityBase的静态构造函数中写入这个模块所共有的代码。
回到目录
初始化数据
有些数据表需要默认初始化一些数据,如类别表、配置表等,便于开发测试。
这个时候可以重载InitData方法,它会在实体类第一次访问数据库之前执行。
这里遇到Meta的第二次用法Meta.Count,该属性表示当前实体类数据表的总行数。
当总行数在100万以内时,数字精确等于 select count(*) from table,大于100万时,将采用特有的快速方法。
Meta.Count带缓存,拥有极好的性能,可用于粗略(数值较小时精确)估算该表总行数。
这里通过Meta.Count来判断该表是否为空表,然后对空表插入一些默认数据。
回到目录
数据验证Valid
每个实体类在Insert/Update之前,都需要Valid验证数据 ,参数isNew以区分Insert。
Valid常常可用于判断主要字段的有效性,无效时强烈推荐抛出参数类异常,魔方NewLife.Cube表单将可以捕获并定位。
除此之外,Valid用得更多的功能是在Insert/Update之前修改完善字段数据,例如上面对密码进行MD5散列,以及格式化RoleIDs。
这里出现新技术,IsDirty和Dirtys,这是XCode的脏数据,前者判断Password字段是否有脏数据(Password被赋予跟原来不想等的值),后者清空Password脏数据。
脏数据是生成Update语句的核心,不脏的字段不会出现在update set 之中,实现部分字段更新,后续有专门章节讲解。
回到目录
重载添删改
实体类的添删改操作都可以重载(Insert/Update/Delete/OnInsert/OnUpdate/OnDelete)
重载后可以做业务代码判断,也可以级联更新其它表,还可以记录添删改操作日志,甚至还可以做假删除(重载OnDelete然后实际执行OnUpdate)
分为两组重载,实际执行顺序是:Insert=>Valid=>EntityModule=>OnInsert
回到目录
扩展属性
XCode不支持多表关联Join,取而代之的是扩展属性!
扩展属性的意义,用到该属性时,再去查询相应数据,一般目标表带有缓存,并且扩展属性Extends也有缓存
一般扩展属性复杂对象加上XmlIgnore和ScriptIgnore特性,规避Xml序列化和Json序列化。
常常还会加上 AbcName 这样的字符串型属性,头上的Map特性将在魔方NewLife.Cube展现数据时发挥极大作用。
__.ClassID表示映射到该字段,在所有显示ClassID的地方用当前属性ClassName替代;
后面的类名和字段名,表示要关联的目标表和字段,在魔方Cube表单中将直接生成下拉选择;
回到目录
扩展查询
实际业务中经常会用到根据某一两个字段查询的需求,例如根据主键查询。
一般我们把查询返回单个对象的方法命名为 FindByAbc,而把返回多个实体的方法命名为 FindAllByAbc。
上面的代码展示了3种查询方法:
通过Meta.Count判断,当总行数小于1000时,全部走Meta.Cache实体缓存表达式搜索,其原理是整表一次性载入内存,后续有专门文章介绍;
FindByID和FindByName,当总数大于1000时,走对象缓存Meta.SingleCache,按主键ID/Name为键,缓存实体对象;
不常用的FindByMail和FindAllByClassID中,用到了真正的数据库查询 Find(__.Mail, mail) 和 FindAll(_.ClassID == classid);
默认生成的代码,都带有实体缓存和对象缓存的例子,默认情况下,FindByID只需要查一次数据并载入内存,即可实现“极速查询”,后续每10秒异步更新。
显然,如果完全不需要用到缓存,直接写数据库代码就好了。
回到目录
高级查询
在业务实现中经常出现超过两个甚至更多查询条件,这个时候我们推荐Search或SearchAbc
XCode的查询有一套条件表达式,以WhereExpression为代表,可以动态拼接任意复杂的where查询语句。
FindAll常用两个参数,第一个条件,第二个PageParameter实现分页查询。
至此,简单罗列了实体类的主要构成,具体各个构成部分都将会在后面有专题文章介绍。
回到目录
系列教程
NewLife.XCode教程系列[2019版]
增删改查入门。快速展现用法,代码配置连接字符串
数据模型文件。建立表格字段和索引,名字以及数据类型规范,推荐字段(时间,用户,IP)
实体类详解。数据类业务类,泛型基类,接口
功能设置。连接字符串,调试开关,SQL日志,慢日志,参数化,执行超时。代码与配置文件设置,连接字符串局部设置
反向工程。自动建立数据库数据表
数据初始化。InitData写入初始化数据
高级增删改。重载拦截,自增字段,Valid验证,实体模型(时间,用户,IP)
脏数据。如何产生,怎么利用
增量累加。高并发统计
事务处理。单表和多表,不同连接,多种写法
扩展属性。多表关联,Map映射
高级查询。复杂条件,分页,自定义扩展FieldItem,查总记录数,查汇总统计
数据层缓存。Sql缓存,更新机制
实体缓存。全表整理缓存,更新机制
对象缓存。字典缓存,适用用户等数据较多场景。
百亿级性能。字段精炼,索引完备,合理查询,充分利用缓存
实体工厂。元数据,通用处理程序
角色权限。Membership
导入导出。Xml,Json,二进制,网络或文件
分表分库。常见拆分逻辑
高级统计。聚合统计,分组统计
批量写入。批量插入,批量Upsert,异步保存
实体队列。写入级缓存,提升性能。
备份同步。备份数据,恢复数据,同步数据
数据服务。提供RPC接口服务,远程执行查询,例如SQLite网络版
大数据分析。ETL抽取,调度计算处理,结果持久化