概述


  实体框架是一个 ORM 框架,它在结构和使用上参考了 Entity Frameworklambda 表达式的解析部份参考了 iqtoolkitNLite 开源框架。

  Fireasy 实体框架与 Entity Framework 非常类似,都可以使用 lambdal 表达式查询,但在操作上却不大相同。Fireasy 实体框架比较快捷,它提供了 Insert、Update、Delete 等方法,而不需要像 Entity Framework 所有的对象都需要状态管控,所有改动都在最后使用 SaveChanage 方法进行提交。

  另外,Fireasy 实体框架本身已经适配了多种数据库,如 SqlServerOracleMySqlSQLitePostgreSqlFirebirdMariaDB(使用 MySql 适配),你只需在 Nuget 管理器 里安装相应数据库的驱动包就可以了,具体可参考 数据库提供者


👆 主要特性

  • 依赖属性:你所看不到的又不得不了解的东西,它的存在为为了能够及时通知属性值的变更,属性的赋值与取值都跟它息息相关。
  • 实体属性及实体集属性:和 Entity Framework 里的导航属性是一个概念,目的是为了实现关联查询。
  • 实体模型IEntity 定义了一组实体的特性,正如上面说的一样, GetValue 和 SetValue 离不开依赖属性,IsModified 可判断属性值是否改变。LightEntity 是目前所采用的基类,它在 EntityContext 初始化时,自动进行了 AOP 代理包装,使实体获得了状态记忆功能。因此你的属性需要定义成 virtual
  • 实体关系:与 Entity Framework 类似,用于定义实体间的关系,主外键名称一致的情况下会自动创建关系,否则需要使用 RelationshipAttributeRelationshipAssignAttribute 关联。
  • 实体上下文:提供类似于 Entity Framework 的数据上下文,即 EntityContext,它一样可以使用 CodeFirst 模式。
  • 实体上下文对象池:可以使用对象池,提高对象的复用率降低创建对象耗用的时间。
  • 实体仓储:每一个实体对应一个仓储,它们分布在 EntityContext 上,提供 lambda 表达式查询、新增、修改、删除等提供。
  • 仓储适配器:默认的适配器是基于 Fireasy.Data 的,你也可以实现其他的适配器,以达到在不改变Entity模型的情况下依然可以使用其他框架的目的,比如 Entity FrameworkMongodb 等等。
  • lambda 查询:支持常用的原生 lambda 查询,如 Where、OrderBy、GroupBy、Join 等等。
  • 逻辑删除标记:实体设置逻辑删除标记后,查询时将自动过滤这些已经被标记的数据。
  • 全局筛选:Apply 方法可以定义针对某实体类型的全局筛选条件。
  • 延迟加载:对于关联属性,可以在需要的时候才从库中读取加载。
  • 惰性加载:对于关联属性,由于延迟加载机制将发生 n+1 次数据库查询动作,此时可以使用Include方法将关联属性预先加载出来。
  • 扩展方法:对 IQueryable 查询的扩展,比较常用的如 Segment、AssertWhere、ExtendAs、BatchOr、BatchAnd、CacheParsing 等等,具体的使用不在这里说明。
  • 树结构:采用类似 00010001 的编码来管理树结构,提供插入、移动、枚举孩子、递归父亲、获取兄弟等方法。
  • 数据验证:基于 DataAnnotations 制定实体的数据验证规则。
  • 持久化事务:基于 Scope 定义线程内的事务控制。
  • 持久化环境:根据环境内的参数,格式化实体所映射的表名称,实现数据表横向扩展。
  • 持久化事件订阅:提供一个订阅器,用于接收持久化事件,如新增、修改、删除等,事件分为 Before 和 After,Before 还可以对该操作进行取消。
  • 动态持久化:通过动态构造实体类型,实现其持久化操作。
  • 解析缓存lambda 表达式解析时,可以通过配置开启自动缓存开关(默认开),开启后,LINQ的解析时间将会有效缩短,该缓存存放于内存中,部分LINQ解析缓存存在问题,可在查询中使用CacheParsing关闭。
  • 查询缓存:查询返回数据时,可以通过配置开启自动缓存开关(默认关),缓存是由 Fireasy.Common 缓存管理器提供的(最好是配置redis)。也可以在查询中使用 CacheExecution 指定是否开启查询缓存。
  • 自定义函数:可在查询中使用自定义方法,该方法与数据库的自定义函数相对应。
  • 方法绑定:可在查询中自定义方法,对该方法进行绑定,即转换为可解析的 lambda 表达式。

👆 结构图

  下面是本章中比较重要的几个接口和类的结构图:



👆 示例代码

public void Sample()
{
    using (var context = new DbContext())
    {
        DateTime? startTime = null;
        DateTime? endTime = null;
        var state = 0;

        //AssertWhere 用法
        var orders = context.Orders
            .AssertWhere(startTime != null, s => s.OrderDate >= startTime)
            .AssertWhere(endTime != null, s => s.OrderDate <= endTime)
            .AssertWhere(state == 0, s => s.RequiredDate == DateTime.Now, s => s.RequiredDate >= DateTime.Now)
            .AsNoTracking();
    
        //ExtandAs 扩展用法
        var details = context.OrderDetails.Select(s =>
            s.ExtendAs<OrderDetails>(() => new OrderDetails
                {
                    ProductName = s.Products.ProductName
                }))
            .ToList();
        
        //分页
        var pager = new DataPager(50, 2);
        var products = context.Products.Segment(pager).ToList();
        
        //排序
        var sorting = new SortDefinition();
        sorting.Member = "OrderDate";
        sorting.Order = SortOrder.Descending;

        var orders1 = context.Orders
            .Select(s => new { s.OrderDate, CompanyName = s.Customers.CompanyName })
            .OrderBy(sorting, u => u.OrderByDescending(s => s.OrderDate))
            .ToList();
        
        //按条件更新
        context.Orders.Update(() => new Orders { Freight = 1 }, s => s.OrderDate >= DateTime.Now);
        
        //计算器方式更新
        context.Orders.Update(s => new Orders { Freight = s.Freight * 100 }, s => s.OrderDate >= DateTime.Now);
        
        //按条件删除
        context.Orders.Delete(s => s.OrderDate > DateTime.Now);

        //Batch插入
        var depts = new List<Depts>();

        for (var i = 0; i < 100; i++)
        {
            var d = Depts.New();
            d.DeptName = "测试" + i;
            depts.Add(d);
        }

        context.Depts.Batch(depts, (u, s) => u.Insert(s));
    }
}