NewLife.XCode是一个有15年历史的开源数据中间件,支持netcore/net45/net40,由新生命团队(2002~2020)开发完成并维护至今,以下简称XCode。

整个系列教程会大量结合示例代码和运行日志来进行深入分析,蕴含多年开发经验于其中,代表作有百亿级大数据实时计算项目。

开源地址:https://github.com/NewLifeX/X (求star, 1077+)


常用数据库操作添删改查,但实际上80%以上场景可能都是查询。XCode内置准备了众多数据查询方法,在泛型实体基类中提供,所有实体类可以直接使用。


查找单行数据

单行查找的本质,就是查找满足条件的第一行数据!

单行查找方法一般叫Find,(取名来自List<T>),返回一个实体对象,根据某条件查找也就是 FindByCode/FindByParentIdAndName 等。

内置单行查找方法:

  • Find(Expression where)。标准用法,构建查询表达式。例 Find(_.Code=="1234abcd")
  • Find(String whereClause)。旧版用法,不推荐。例 Find("Code=1234abcd")
  • Find(String name, Object value)。清爽用法。例 Find(__.Code, "1234abcd")
  • Find(String[] names, Object[] values)。臃肿用法,不推荐。例 Find(new []{"ParendId", "Name"}, new []{1, "role"})
  • FindByKey(Object key)。主键查找,不推荐。例 FindByKey("1234abcd")
  • FindByKeyForEdit(Object key)。为编辑而查找,魔方专用。
  • FindMin(String field, Expression where = null)。查找指定字段的最小值
  • FindMax(String field, Expression where = null)。查找指定字段的最大值


自2006年以来,经过14年发展表明,最有价值的是 Find(Expression where) 。其它方法将会逐步淘汰。

注意:

下划线“_”是每个实体类都拥有的内嵌类,它用于快速访问该实体类的每个字段元数据,例如 _.Code 。既然是内嵌类,那么就需要在实体类内部才能直接使用,所以一般建议在Biz业务类内部写各种自定义的查询方法,代码可以很精简。如果是在实体类外部使用,就得假设实体类类名了,例如 User._.Code。如果外部使用,还可以使用 using static XCode.Membership.User;,然后该外部代码也可以直接使用 _.Code。


查找多行数据

多行查询是XCode最重要的功能,没有之一!

多行查找一般叫FindAll,(取名来自List<T>),返回一个实体列表 IList<TEntity>,即使没有数据也返回空列表,该约定确保使用者可以不必判空。根据某些条件查找也就是 FindAllByRoleId 等。

多年经验,最强最好用的查询方法:

IList<TEntity> FindAll(Expression where, PageParameter page = null, String selects = null)
  • where查询条件,借助WhereExpression构建复杂表达式
  • page 分页与排序。
    • 若RetrieveTotalCount=true,内部除了查数据,还执行一次FindCount把满足条件的总数返回TotalCount
    • 若RetrieveState=true,内部还将根据 Meta.Factory.SelectStat 执行一次聚合统计,结果返回State,一般用于数据求和求平均等。
  • selects  指定要查询的字段,默认null查询所有字段

例子:

var list1 = User.FindAll(_.Code.StartsWith("021.") & _.Enable==true, null, null);
var list2 = User.FindAll(_.RoleId==1 & _.CreateTime>DateTime.ToDay, new PageParameter{PageSize=100});


过去很长一段时间里,用得最多的5参数版,同时也是所有FindAll的最底层:

IList<TEntity> FindAll(String where, String order, String selects, Int64 startRowIndex, Int64 maximumRows)

最经典的批量查询,看这个 Select @selects From Table Where @where Order By @order Limit @startRowIndex,@maximumRows ,你就明白各参数的意思了。内部就是为了构造这么一个语句,所以,继续在where里面写groupby和having也是可以的。


内置的其它多行查询方法:

  • FindAll()  查全表,数据量大于1000时慎用
  • FindAll(String sql)  直接写sql,大招
  • FindAll(Expression where, String order, String selects, Int64 startRowIndex, Int64 maximumRows)  过渡版,很少用
  • DbTable FindData(Expression where, String order, String selects, Int64 startRowIndex, Int64 maximumRows)  只是查出来数据集,并没有填充到实体类。DbTable可以轻易保存为二进制文件并恢复。
  • FindAllWithCache()  实体缓存里的全部数据,它会把整个数据表缓存到内存,不建议在大于1000行的表上使用。

查找记录数(分页基础)

查找记录数一般叫FindCount。早期为了适配 ObjectDataSource ,FindCount与FindAll配套出现,并且参数名一致。实际上,对于FindCount来说,分页和排序参数是没有任何意义的。

  • FindCount(Expression where, String order = null, String selects = null, Int64 startRowIndex = 0, Int64 maximumRows = 0)  标准用法
  • FindCount(String where, String order = null, String selects = null, Int64 startRowIndex = 0, Int64 maximumRows = 0)  传统用法

在以前,FindCount 主要用于查记录数作为分页或者判断数据是否存在的依据;

在今天,FindCount 已经几乎没有什么使用价值,判断数据是否存在时,直接FindAll把数据查出来更简单,尽管稍微有点浪费,但XCode的超高性能不就是用来给开发者浪费的吗?


在ASP.Net MVC和asp.net core 下,查记录数一般在 FindAll 时设置 page.RetrieveTotalCount=true,两个事情一块做了。

获取查询SQL

XCode不支持多表关联Join,实在需要,可以用子查询替代。而复杂的子查询,就需要借助FindSQL了

SelectBuilder FindSQL(String where, String order, String selects, Int32 startRowIndex = 0, Int32 maximumRows = 0)
    SelectBuilder FindSQLWithKey(String where = null)

FindSQL操作并不会执行数据库查询操作,仅仅是构建一个SQL子句。


例如,查找幼儿园中班的所有班男生:

var list = Student.FindAll(_.ClassId.In(Class.FindSQLWithKey(Class._.Type=="中班")) & _.Sex==SexKinds.男);

查询生成的语句:

select * from Student where ClassId in (select id from Class where type='中班') and sex=1


注意,强烈建议Student.Biz.cs封装SearchByClassType方法,Class.Biz.cs封装FindSQLByType方法,以充分利用内嵌类“_”。

// Student.Biz.cs
public static IList<Student> SearchByClassType(String type, SexKinds sex)
    => FindAll(_.ClassId.In(Class.FindSQLByType(type)) & _.Sex==sex);

// Class.Biz.cs
public static SelectBuilder FindSQLByType(String type)
    => FindSQLWithKey(_.Type==type);


填充数据

所有查询的本质,都是生成SQL跟DAL层执行,得到DbTable/DataSet/DataTable后,由实体类LoadData加载映射成为实体对象。

  • IList<TEntity> LoadData(DbTable ds)  加载DbTable,XCode标配
  • IList<TEntity> LoadData(DataSet ds)  传统
  • IList<TEntity> LoadData(DataTable dt)  传统
  • IList<TEntity> LoadData(IDataReader dr)  传统

因此,完全可以用其它方法查到数据集,然后经由LoadData加载,得到实体对象。


也可以自己实现 IDataRowEntityAccessor 接口,设置到 Meta.Factory.Accessor 来改变加载数据的行为。

高级查询

实体基类Entity<TEntity>内置了一套高级查询方法:

IList<TEntity> Search(DateTime start, DateTime end, String key, PageParameter page);
WhereExpression SearchWhere(DateTime start, DateTime end, String key, PageParameter page);

该方法支持根据主时间字段所在区间,以及关键字,进行分页排序查询。

  • 主时间。Meta.Factory.MasterTime,默认为第一个出现在索引首位的时间字段,一般是UpdateTime
  • 关键字。默认构造为所有字符串字段的Contains操作,也就是 '%key%'


例如:

Select * From Node Where LastLogin>='2020-03-25 00:00:00' And LastLogin<'2020-03-26 00:00:00' And (Name Like '%win%' Or Code Like '%win%' Or Secret Like '%win%' Or Version Like '%win%' Or OS Like '%win%' Or OSVersion Like '%win%' Or MachineName Like '%win%' Or UserName Like '%win%' Or Processor Like '%win%' Or CpuID Like '%win%' Or Uuid Like '%win%' Or MachineGuid Like '%win%' Or MACs Like '%win%' Or InstallPath Like '%win%' Or Runtime Like '%win%' Or Address Like '%win%' Or LastLoginIP Like '%win%' Or CreateIP Like '%win%' Or UpdateIP Like '%win%' Or Remark Like '%win%') Order By ID Desc limit 20


自定义高级查询的更多用法请阅读

此处为内容卡片,点击链接查看:https://newlifex.com/xcode/search


作者:大石头 发布:2021-11-19 11:09:04 浏览:5,102)