基于EF的KYSharpCore底层框架实现

此项目说明为公司内部,外部引用不了所需要的文件。

一、怎么用

首先我们先讲下,怎么搭建和使用这个框架。
本次案例采用的是

KySharpCode 1.0.2 版本,(目前已经升级到1.2版本)

KYSharpCore.EntityFrameworkCore 2.0.0.0 版本,(目前已经升级到3.1.7.4版本)

KYSharpCore.EntityFrameWork.MySql 2.0.0.0版本,(目前已经升级到3.1.7.4版本)

EF采用的是2.2.6 版本(目前采用到3.1版本)

数据库采用 mysql

先创建一个demo的mvc项目

 

 

 

接着创建一个实体库,实体的类库是通过参数的方式传递的,使用默认的KYSharpCore.EntityFrameWork.MySql 连接时,是appseting.json中配置。

类库需要引用 KySharpCore 程序集,直接nuget上引用

 

 

 

定义一个实体类 Users,当然对应,数据库中的结构要手动创建,这个示例里面没有采用codefirst模式。

  public class Users : EntityBase<string>
    {
        public string Name { get; set; }

    }

实体需要继承 EntityBase 基类,EntityBase 基类只定义了一个主键Id字段,其中 <string> 表示Id主键的类型为string类型,系统会自动生成Guid 的字符串。

然后再回到我们的web页面demo项目,添加实体引用。

 

 

添加数据库操作库

 

 

 

这样,我们就完成了引用的内容。

接下来做些数据库配置

1、数据库配置

打开appsettings.json 文件,增加数据库信息配置

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnectionString": "server=192.168.20.134;port=3306;database=eftest;uid=root;pwd=3BIW#lP211HkB4Yq;CharSet=utf8;",
  "Models": "EFWebTest.Model" //实体类库的名称,用于反射使用,多个用英文逗号隔开
} }
DefaultConnectionString为默认的名称,不能修改。

2、Startup 类中完成注入

   public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            //注册默认数据库

              //注册程序集
               services.AddKYSharpCoreDbContext(Configuration); //注入数据库连接配置
               services.AddKySharpService(new string[] { "EFWebTest.Service" }); //注入服务层



            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

//注册默认数据库
services.AddKYSharpCoreDbContext(Configuration);
只需要增加这行的代码,即可实现数据库上下文的注册

至此,准备工作就已经好了。

 services.AddKySharpService(new string[] { "EFWebTest.Service" }); //注入服务层

服务层必须是有继承接口,未继承接口的类不会注入。

 

kysharcore 1.2版本更新,对服务层增加了一下功能,当KYSharpCore.EntityFrameWork.MySql未升级时(目前3.1.4.7使用的kysharcore版本还是1.1,需要服务层中单独再次引用1.2版本)

 

 增加了可以指定注入的依赖周期,提供依赖注入的3种生命周期。

未指定时,默认为IScopeDependency类型,(1.2以前的版本默认是瞬时类型),建议更新

 

 

 

下面,我们来看看怎么使用。

打开Home控制器,采用构造函数注入User

     private readonly IRepository<Users, string> _userRepository;

        public HomeController(IRepository<Users, string> userRepository)
        {
            _userRepository = userRepository;
        }

 

   public async Task<IActionResult> Index()
        {
            //插入一个用户信息
            var m=new Users() { Name = "令狐冲"};
           int i=await _userRepository.InsertAsync(m);

            return Content(i.ToString());
        }

 

运行起来,然后看看数据是否写入

 

 

 这时候,我们就看到了,数据写入了。

至此,也说明,我们连接数据库操作成功。

 

二、操作方法说明

下面,我们 重点来说明下,仓储层,都提供了哪些操作方法

首先,我们上接口源码

    /// <summary>
    ///  仓储层接口
    /// </summary>
    /// <typeparam name="TEntity">实体对象</typeparam>
    /// <typeparam name="TKey">主键类型</typeparam>
    public interface IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>
    {
        #region 同步方法

        /// <summary>
        /// 插入实体
        /// </summary>
        /// <param name="entities"></param>
        int Insert(params TEntity[] entities);

        /// <summary>
        /// 删除实体集合
        /// </summary>
        /// <param name="entities"></param>
        int Delete(params TEntity[] entities);


        /// <summary>
        /// 删除实体集合
        /// </summary>
        /// <param name="entities"></param>
        int DeleteRange(TEntity[] entities);

        /// <summary>
        /// 批量删除符合查询条件的数据
        /// </summary>
        /// <param name="predicate">查询条件表达式</param>
        /// <returns></returns>
        int DeleteBatch(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 按主键删除
        /// </summary>
        /// <param name="key"></param>
        int Delete(TKey key);


        /// <summary>
        /// 更新数据,当非跟踪状态下时,需要更改数据时,需要执行此方法
        /// </summary>
        /// <param name="entities">实体对象集合</param>
        int Update(params TEntity[] entities);
        /// <summary>
        /// 获取符合条件的第一条
        /// </summary>
        /// <param name="predicate">查询条件,lambda表达式</param>
        /// <returns></returns>
        TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 统计符合条件的记录数
        /// </summary>
        /// <param name="predicate">查询条件,lambda表达式</param>
        /// <returns>返回符合的记录数</returns>
        int Count(Expression<Func<TEntity, bool>> predicate);


        /// <summary>
        /// 统计符合条件的记录数
        /// </summary>
        /// <param name="predicate">查询条件,lambda表达式</param>
        /// <returns>返回符合的记录数</returns>
        long LongCount(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 查找指定主键的实体
        /// </summary>
        /// <param name="key">实体主键</param>
        /// <returns>符合主键的实体,不存在时返回null</returns>
        TEntity Get(TKey key);


        /// <summary>
        /// 获取<typeparamref name="TEntity"/>不跟踪数据更改(NoTracking)的查询数据源
        /// </summary>
        /// <returns>符合条件的数据集</returns>
        IQueryable<TEntity> Query();

        /// <summary>
        /// 查询数据,不跟踪
        /// </summary>
        /// <param name="predicate">查询表达式</param>
        /// <returns></returns>
        IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate);


        /// <summary>
        /// 获取数据,不跟踪
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <param name="includes">导航属性</param>
        /// <returns></returns>
        IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// 数据查询,跟踪
        /// </summary>
        /// <returns></returns>
        IQueryable<TEntity> TrackQuery();
        /// <summary>
        /// 获取<typeparamref name="TEntity"/>跟踪数据更改(Tracking)的查询数据源
        /// </summary>
        /// <returns>符合条件的数据集</returns>
        IQueryable<TEntity> TrackQuery(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 获取<typeparamref name="TEntity"/>跟踪数据更改(Tracking)的查询数据源,并可Include导航属性
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <param name="includes">要Include操作的属性表达式</param>
        /// <returns>符合条件的数据集</returns>
        IQueryable<TEntity> TrackQuery(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// 分页,不跟踪数据
        /// </summary>
        /// <param name="pageIndex">当前请求的页码</param>
        /// <param name="pageSize">每页返回的记录数</param>
        /// <param name="predicate">查询条件表达式</param>
        /// <param name="sortConditions">排序对象</param>
        /// <param name="selector">要返回的字段</param>
        /// <param name="includes">导航属性</param>
        /// <returns>返回符合条件的数据和条数,返回pageData对象类型,包含Count和Data</returns>
        PageData<TEntity> Page(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate, List<SortCondition<TEntity>> sortConditions, Expression<Func<TEntity, TEntity>> selector = null, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// 分页,不跟踪数据
        /// </summary>
        /// <param name="pageIndex">当前请求的页码</param>
        /// <param name="pageSize">每页返回的记录数</param>
        /// <param name="predicate">查询条件表达式</param>
        /// <param name="sortConditions">排序对象</param>
        /// <param name="includes">导航属性</param>
        /// <returns>返回符合条件的数据和条数,返回pageData对象类型,包含Count和Data</returns>
        PageData<TEntity> Page(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate, List<SortCondition<TEntity>> sortConditions, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// 分页,跟踪数据
        /// </summary>
        /// <param name="pageIndex">当前请求的页码</param>
        /// <param name="pageSize">每页返回的记录数</param>
        /// <param name="predicate">查询条件表达式</param>
        /// <param name="sortConditions">排序对象</param>
        /// <param name="selector">返回的字段</param>
        /// <param name="includes">导航属性</param>
        /// <returns>返回符合条件的数据和条数,返回pageData对象类型,包含Count和Data</returns>
        PageData<TEntity> TrackPage(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate, List<SortCondition<TEntity>> sortConditions, Expression<Func<TEntity, TEntity>> selector = null, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// 分页,跟踪数据
        /// </summary>
        /// <param name="pageIndex">当前请求的页码</param>
        /// <param name="pageSize">每页返回的记录数</param>
        /// <param name="predicate">查询条件表达式</param>
        /// <param name="sortConditions">排序对象</param>
        /// <param name="includes">导航属性</param>
        /// <returns>返回符合条件的数据和条数,返回pageData对象类型,包含Count和Data</returns>
        PageData<TEntity> TrackPage(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate, List<SortCondition<TEntity>> sortConditions, params Expression<Func<TEntity, object>>[] includes);


        #endregion 同步方法

        #region 异步

        /// <summary>
        /// 插入实体
        /// </summary>
        /// <param name="entities"></param>
        Task<int> InsertAsync(params TEntity[] entities);

        /// <summary>
        /// 删除实体集合
        /// </summary>
        /// <param name="entities"></param>
        Task<int> DeleteAsync(params TEntity[] entities);


        /// <summary>
        /// 删除实体集合
        /// </summary>
        /// <param name="entities"></param>
        Task<int> DeleteRangeAsync(TEntity[] entities);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        Task<int> DeleteBatchAsync(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 按主键删除
        /// </summary>
        /// <param name="key"></param>
        Task<int> DeleteAsync(TKey key);


        /// <summary>
        /// 更新数据,当非跟踪状态下时,需要更改数据时,需要执行此方法
        /// </summary>
        /// <param name="entities">实体对象集合</param>
        Task<int> UpdateAsync(params TEntity[] entities);
        /// <summary>
        /// 获取符合条件的第一条
        /// </summary>
        /// <param name="predicate">查询条件,lambda表达式</param>
        /// <returns></returns>
        Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 统计符合条件的记录数
        /// </summary>
        /// <param name="predicate">查询条件,lambda表达式</param>
        /// <returns>返回符合的记录数</returns>
        Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate);


        /// <summary>
        /// 统计符合条件的记录数
        /// </summary>
        /// <param name="predicate">查询条件,lambda表达式</param>
        /// <returns>返回符合的记录数</returns>
        Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 查找指定主键的实体
        /// </summary>
        /// <param name="key">实体主键</param>
        /// <returns>符合主键的实体,不存在时返回null</returns>
        Task<TEntity> GetAsync(TKey key);



        /// <summary>
        /// 分页,不跟踪
        /// </summary>
        /// <param name="pageIndex">当前请求的页码</param>
        /// <param name="pageSize">每页记录数</param>
        /// <param name="predicate">查询条件表达式</param>
        /// <param name="sortConditions">排序条件</param>
        /// <param name="selector">返回的字段内容</param>
        /// <param name="includes">导航属性</param>
        /// <returns>返回总条数和符合的记录数</returns>
        Task<PageData<TEntity>> PageAsync(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate, List<SortCondition<TEntity>> sortConditions, Expression<Func<TEntity, TEntity>> selector = null, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// 分页,不跟踪
        /// </summary>
        /// <param name="pageIndex">当前请求的页码</param>
        /// <param name="pageSize">每页记录数</param>
        /// <param name="predicate">查询条件表达式</param>
        /// <param name="sortConditions">排序条件</param>
        /// <param name="includes">导航属性</param>
        /// <returns>返回总条数和符合的记录数</returns>
        Task<PageData<TEntity>> PageAsync(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate, List<SortCondition<TEntity>> sortConditions, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// 分页,跟踪
        /// </summary>
        /// <param name="pageIndex">当前请求的页码</param>
        /// <param name="pageSize">每页记录数</param>
        /// <param name="predicate">查询条件表达式</param>
        /// <param name="sortConditions">排序条件</param>
        /// <param name="includes">要包含的导航属性</param>
        /// <param name="selector">返回的字段内容</param>
        /// <returns>返回总条数和符合的记录数</returns>
        Task<PageData<TEntity>> PageTrackAsync(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate, List<SortCondition<TEntity>> sortConditions, Expression<Func<TEntity, TEntity>> selector = null, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// 分页,跟踪
        /// </summary>
        /// <param name="pageIndex">当前请求的页码</param>
        /// <param name="pageSize">每页记录数</param>
        /// <param name="predicate">查询条件表达式</param>
        /// <param name="sortConditions">排序条件</param>
        /// <param name="includes">要包含的导航属性</param>
        /// <returns>返回总条数和符合的记录数</returns>
        Task<PageData<TEntity>> PageTrackAsync(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate, List<SortCondition<TEntity>> sortConditions, params Expression<Func<TEntity, object>>[] includes);


        #endregion 异步

    }

 

 

通过接口类源码,我们可以很直观的看到整个数据操作提供的所有函数。 

至于使用,也很简单,都是仓储对象.方法名  比如 _userRepository.InsertAsync

但有几点需要说明

1、所有的增删改的方法,都是直接执行的,也就是非事务提交,如执行3个写入方法,前2个成功,第3个失败,则数据不会回滚,前2个数据会进数据库,第3个数据不进库。

那如果要事务提交的话,该如何处理?

采用UnitOfWork来定义上下文和定义事务。看下面示例

   private readonly IRepository<Users, string> _userRepository;
        private readonly IUnitOfWork _unitOfWork;
        public HomeController(IRepository<Users, string> userRepository, IUnitOfWork unitOfWork)
        {
            _userRepository = userRepository;
            _unitOfWork = unitOfWork;
        }

构造函数注入 unitOfWork 对象

public async Task<IActionResult> Index()
        {
            //开启事务
            _unitOfWork.BeginTransaction();
            //插入一个用户信息
            var m = new Users() { Name = "令狐冲" };
            int i = await _userRepository.InsertAsync(m);
            //提交事务
            _unitOfWork.Commit();

            return Content(i.ToString());
        }

 

从上面的代码我们可以看到,在最前面,我们开启了事务,最后提交整个事务。这时,就能达到事务提交的目的。如上面举的例子,3个都将回滚,一个都不会写入到数据库中。

 

2、查询默认是非跟踪状态

为了提供整体查询性能,查询默认都采用AsNoTracking 模式,若要跟踪,则可使用Track开头的方法,比如 TrackQuery 方法。

 

3、数据的update修改

 在跟踪状态下执行修改,只需要对字段重新赋值,然后执行SaveChanges方法即可。

例如下面的代码

 public async Task<IActionResult> Index()
        {
            //查询出用户
            var user = await _userRepository.TrackQuery(m => m.Name == "令狐冲").FirstOrDefaultAsync();
            if (user != null)
            {
                user.Name = "张无忌";
                //执行提交

                await _unitOfWork.SaveChangesAsync();
            }

            return Content("Hello World");
        }

运行起来

 

 

 

 

我们看到 原来的令狐冲,已经修改为 张无忌了。

那如果是非跟踪状态下呢,即把 TrackQuery 方法替换成 Query,这时候代码是不会执行变更的,因为没有跟踪。

非跟踪状态下,就需要采用update方法,如

public async Task<IActionResult> Index()
        {
            //查询出用户
            var user = await _userRepository.Query(m => m.Id == "b29bf6f3e8144cb59952ab4900fd042a").FirstOrDefaultAsync();
            if (user != null)
            {
                user.Name = "段誉";
                //执行提交
                await _userRepository.UpdateAsync(user);
            }

            return Content("Hello World");
        }

变化的地方,我已经标红了。

运行起来

 

 

我们看到,数据变更过来了,在执行update的时候就直接提交。

若是多个update,为了事务执行,还是需要跟上面一样,引入unitofwork的事务提交方式(前面开启事务,最后提交事务)。

4、所有的查询返回,默认是 IQueryable 类型,方面开发人员自行转换和提高性能。

 

5、分页查询

分页采用的是定义了一个新的对象PageData 来进行接收,该对象有两个变量

/// <summary>
    /// 当异步分页时,不能使用out和ref,需要定义变量来存储Count和数据
    /// </summary>
   public class PageData<T> where T:class
    {
        /// <summary>
        /// 总条数
        /// </summary>
        public int Count { get;set;}

        /// <summary>
        /// 符合条件的记录
        /// </summary>
        public IEnumerable<T> Data { get; set; }
    }
Count,返回总条数,Data返回的数据。
需要注意的是,分页返回,直接返回泛型列表数据,而非IQueryable类型。



以上,我们就介绍完了这个框架的使用。

demo地址:https://github.com/fei686868/KySharpCodeDemo



posted @ 2020-01-21 15:45  黄明辉  阅读(185)  评论(0)    收藏  举报