写在前面:

一直都想自己从零搭建一个项目框架,来熟悉.net Core 与DDD框架,奈何技术不过关,这个计划一直被无限期的咽喉,最近下定决定要完成这件事情。正好运用最近的.NET 5与稍微简单一点的仓储模式来搭建一个完整的博客系统 …本文仅用作个人学习参考,如有不对之处,请批评指正!

一、引入EF Core+Code First

1.新增类库Model层

image.png
此层用于项目中的实体类,项目中所有有关实体类,均在此层进行添加

2.新增类库EntityFrameworkCore

image.png

项目结构:
EFDBContext:EF Core上下文基类
Migrations:Code First迁移文件夹,自动生成

EFDBContext.cs:

  1. public class EFDBContext : DbContext
  2. {
  3. public EFDBContext(DbContextOptions<EFDBContext> options) : base(options)
  4. {
  5. }
  6. public EFDBContext()
  7. {
  8. }
  9. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  10. {
  11. base.OnConfiguring(optionsBuilder);
  12. }
  13. #region Entity DbSest<> 注:新增一个实体类都需要在下面写上,否则不会迁移到数据库中
  14. public virtual DbSet<SysUserInfo> SysUserInfo { get; set; }
  15. public virtual DbSet<BlogArticle> BlogArticle { get; set; }
  16. #endregion
  17. }

3.WebAPI层中Startup.cs类下ConfigureServices方法中添加数据库连接字符串

public void ConfigureServices(IServiceCollection services)
        {

            services.AddControllers(); 

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Blog.WebAPI", Version = "v1" });


                #region 读取xml信息
                // 使用反射获取xml文件,并构造出文件的路径
                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                // 启用xml注释,该方法第二个参数启用控制器的注释,默认为false.
                c.IncludeXmlComments(xmlPath, true);
                #endregion
            });

            services.AddDbContext<EFDBContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));

        }

ConnectionStrings:DefaultConnection此连接字符串为appsettings.json中配置的连接字符串。

4.新增实体类,添加迁移,更新至数据库中

实体类直接可以到Model层中去新增所需实体类,这里就省略啦…
添加迁移:
工具->NuGet包管理器->程序包管理器控制台:
image.png
下面这一步很关键,程序包控制台中需要选中我们EFDBContext所在的项目,而解决方案中需要将我们配置连接字符串的项目作为启动项目
image.png
Add-Migrations (迁移名称):执行完成之后会自动生成迁移的类
Update-Database:此命令执行完成之后数据库就已经被同步更新啦
这一步完成之后,项目与数据库连接就已经完成啦,下面我们开始逐步搭建我们的仓储模式。

二、搭建IRepository层

image.png
作用:此层为仓储接口层,定义对数据操作的契约(命令执行、重载不同查询结果集合、查询实体、查询返回结果值)。

1、新增文件夹Base,在Base文件夹下新增IBaseRepository.cs接口,此接口为一个泛型仓储接口,定义对数据库的操作

IBaseRepository.cs

public interface IBaseRepository<T> where T : class, new()
    {
        /// <summary>
        /// 新增
        /// </summary>
        /// <param name="entity">单个实体</param>
        /// <returns></returns>
        ValueTask<EntityEntry<T>> Add(T entity);

        /// <summary>
        /// 批量新增
        /// </summary>
        /// <param name="models"></param>
        void AddAll_BulkInsertNew(List<T> models);

        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="whereLambda">需要更新的数据源</param>
        /// <param name="entity">要更新的字段</param>
        /// <returns></returns>
        Task<int> ModifyBy_GaoXiao(Expression<Func<T, bool>> whereLambda, Expression<Func<T, T>> entity);
        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="whereLambda">条件</param>
        /// <returns></returns>
        Task<int> DelBy_GaoXiao(Expression<Func<T, bool>> whereLambda);

        /// <summary>
        /// 是否存在
        /// </summary>
        /// <param name="whereLambda">条件</param>
        /// <returns></returns>
        Task<bool> IsExist(Expression<Func<T, bool>> whereLambda);
        /// <summary>
        /// 获取实体,返回第一个实体对象
        /// </summary>
        /// <param name="whereLambda">条件</param>
        /// <returns></returns>
        Task<T> GetFirstOrDefaultBy(Expression<Func<T, bool>> whereLambda);

        /// <summary>
        /// 获取实体,返回最后一个实体对象
        /// </summary>
        /// <param name="whereLambda">条件</param>
        /// <returns></returns>
        Task<T> GetLastOrDefaultBy(Expression<Func<T, bool>> whereLambda);

        /// <summary>
        /// 获取分页集合
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <param name="pageSize">每页条数</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="whereLambda">条件</param>
        /// <param name="orderByLambda">排序条件</param>
        /// <param name="isAsc">是否顺序</param>
        /// <returns></returns>
        Task<Tuple<List<T>, int>> GetPagedList<S>(int pageSize, int pageIndex, Expression<Func<T, bool>> whereLambda, Expression<Func<T, S>> orderByLambda, bool isAsc);



        #region 
        /// <summary>
        /// 返回查询实体(还未查询数据库)
        /// </summary>
        /// <param name="whereLambda"></param>
        /// <returns></returns>
        System.Linq.IQueryable<T> GetIqueryableBy(Expression<Func<T, bool>> whereLambda);

        System.Linq.IQueryable<T> GetIqueryableBy_AsNoTracking(Expression<Func<T, bool>> whereLambda);

        #endregion

        Task<int> GetCountBy(Expression<Func<T, bool>> whereLambda);


        Task<int> ExcuteSql(string strSql, params object[] paras); 

        DataTable SqlQueryForDataTatable(string strSql, params SqlParameter[] parameters);

    }

2、新建一个工作单元文件夹UnitOfWork,然后新建IunitOfWork.cs接口

此接口主要定义获取DbContext对象,以及对保存对数据库操作
IUnitOfWork.cs

public interface IUnitOfWork
    {
        EFDBContext GetDbContext();
        Task<int> SaveChangesAsync();

    }

ITransaction.cs这里先买一个伏笔,后面会详细说明,此接口主要定义事务操作。

三、搭建Repository层

image.png
作用:此层为一个管理数据持久层,主要实现仓储接口层,是具体对数据库操作的方法实现,同时对工作单元接口的实现,负责数据的CRUD(Create, Read, Update, Delete)

1、新建Base文件夹,新建BaseRepository.cs类

BaseRepository.cs

public class BaseRepository<T> where T : class, new()
    {
        private EFDBContext myDbContext;
        public BaseRepository(EFDBContext myDbContext)
        {
            this.myDbContext = myDbContext;
        }

        #region 新增
        public async ValueTask<EntityEntry<T>> Add(T entity)
        {
            return await myDbContext.Set<T>().AddAsync(entity);
        }

        public async void AddAll_BulkInsertNew(List<T> models)
        {
            await myDbContext.BulkInsertAsync(models);
        }
        #endregion

        #region 修改
        public async Task<int> ModifyBy_GaoXiao(Expression<Func<T, bool>> whereLambda, Expression<Func<T, T>> entity)
        {
            return await myDbContext.Set<T>().Where(whereLambda).UpdateFromQueryAsync(entity);

        }
        #endregion

        #region 删除
        public async Task<int> DelBy_GaoXiao(Expression<Func<T, bool>> whereLambda)
        {
            return await myDbContext.Set<T>().Where(whereLambda).DeleteFromQueryAsync();
        }
        #endregion

        #region 查询
        public async Task<int> GetCountBy(Expression<Func<T, bool>> whereLambda)
        {
            return await myDbContext.Set<T>().Where(whereLambda).CountAsync();
        }

        public async Task<T> GetFirstOrDefaultBy(Expression<Func<T, bool>> whereLambda)
        {
            return await myDbContext.Set<T>().Where(whereLambda).FirstOrDefaultAsync();
        }

        public async Task<T> GetLastOrDefaultBy(Expression<Func<T, bool>> whereLambda)
        {
            return await myDbContext.Set<T>().Where(whereLambda).LastOrDefaultAsync();
        }
        public System.Linq.IQueryable<T> GetIqueryableBy(Expression<Func<T, bool>> whereLambda)
        {
            return myDbContext.Set<T>().Where(whereLambda);
        }

        public System.Linq.IQueryable<T> GetIqueryableBy_AsNoTracking(Expression<Func<T, bool>> whereLambda)
        {
            return myDbContext.Set<T>().AsNoTracking().Where(whereLambda);
        }


        public async Task<Tuple<List<T>, int>> GetPagedList<S>(int pageSize, int pageIndex, System.Linq.Expressions.Expression<Func<T, bool>> whereLambda, System.Linq.Expressions.Expression<Func<T, S>> orderByLambda, bool isAsc)
        {
            var total = await myDbContext.Set<T>().Where(whereLambda).CountAsync();

            if (isAsc)
            {
                var entities = await myDbContext.Set<T>().Where(whereLambda)
                                      .OrderBy<T, S>(orderByLambda)
                                      .Skip(pageSize * (pageIndex - 1))
                                      .Take(pageSize).ToListAsync();

                return new Tuple<List<T>, int>(entities, total);
            }
            else
            {
                var entities = await myDbContext.Set<T>().Where(whereLambda)
                                      .OrderByDescending<T, S>(orderByLambda)
                                      .Skip(pageSize * (pageIndex - 1))
                                      .Take(pageSize).ToListAsync();

                return new Tuple<List<T>, int>(entities, total);
            }
        }

        public async Task<bool> IsExist(Expression<Func<T, bool>> whereLambda)
        {
            return await myDbContext.Set<T>().AnyAsync(whereLambda);
        }




        #region 执行sql语句
        public async Task<int> ExcuteSql(string strSql, params object[] paras)
        {
            return await myDbContext.Database.ExecuteSqlRawAsync(strSql, paras);
        }



        public DataTable SqlQueryForDataTatable(string strSql, params SqlParameter[] parameters)
        {
            SqlConnection conn = new SqlConnection();
            //string connString = System.Configuration.ConfigurationManager.ConnectionStrings["connForDataTable"].ConnectionString;
            conn.ConnectionString = myDbContext.Database.GetConnectionString();
            if (conn.State != ConnectionState.Open)
            {
                conn.Open();
            }
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = conn;
            cmd.CommandText = strSql;
            if (parameters.Length > 0)
            {
                foreach (var item in parameters)
                {
                    cmd.Parameters.Add(item);
                }
            }
            SqlDataAdapter adapter = new SqlDataAdapter(cmd);
            DataTable table = new DataTable();
            adapter.Fill(table);

            adapter.Dispose();
            cmd.Dispose();
            conn.Close();
            conn.Dispose();

            return table;
        }
        #endregion
        #endregion
    }

2、新建UnitOfWork文件夹,该文件夹下新建UnitOfWork.cs类,此类将负责创建数据上下文实例并移交到控制器的所有repository实例

UnitOfWork.cs

public class UnitOfWork : IUnitOfWork
    {
        private readonly EFDBContext _myDbContext;

        public UnitOfWork(EFDBContext myDbContext)
        {
            this._myDbContext = myDbContext;
        }

        public EFDBContext GetDbContext()
        {
            return _myDbContext;
        }

        public async Task<int> SaveChangesAsync()
        {
            return await _myDbContext.SaveChangesAsync();
        }
    }

四、搭建IServices层

image.png
作用:此层为服务接口层,主要定义具体业务逻辑。

1、创建文件夹Base,该文件夹下添加IBaseServices.cs接口

IBaseservices.cs

public interface IBaseServices<T> where T : class
    {
        /// <summary>
        /// 新增
        /// </summary>
        /// <param name="entity">单个实体</param>
        /// <returns></returns>
        Task<int> Add(T entity);


        /// <summary>
        /// 批量新增
        /// </summary>
        /// <param name="models"></param>
        void AddAll_BulkInsertNew(List<T> models);

        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="whereLambda">需要更新的数据源</param>
        /// <param name="entity">要更新的字段</param>
        /// <returns></returns>
        Task<int> ModifyBy_GaoXiao(Expression<Func<T, bool>> whereLambda, Expression<Func<T, T>> entity);
        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="whereLambda">条件</param>
        /// <returns></returns>
        Task<int> DelBy_GaoXiao(Expression<Func<T, bool>> whereLambda);

        /// <summary>
        /// 是否存在
        /// </summary>
        /// <param name="whereLambda">条件</param>
        /// <returns></returns>
        Task<bool> IsExist(Expression<Func<T, bool>> whereLambda);
        /// <summary>
        /// 获取实体,返回第一个实体对象
        /// </summary>
        /// <param name="whereLambda">条件</param>
        /// <returns></returns>
        Task<T> GetFirstOrDefaultBy(Expression<Func<T, bool>> whereLambda);

        /// <summary>
        /// 获取实体,返回最后一个实体对象
        /// </summary>
        /// <param name="whereLambda">条件</param>
        /// <returns></returns>
        Task<T> GetLastOrDefaultBy(Expression<Func<T, bool>> whereLambda);

        /// <summary>
        /// 获取分页集合
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <param name="pageSize">每页条数</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="whereLambda">条件</param>
        /// <param name="orderByLambda">排序条件</param>
        /// <param name="isAsc">是否顺序</param>
        /// <returns></returns>
        Task<Tuple<List<T>, int>> GetPagedList<S>(int pageSize, int pageIndex, Expression<Func<T, bool>> whereLambda, Expression<Func<T, S>> orderByLambda, bool isAsc);



        #region 
        /// <summary>
        /// 返回查询实体(还未查询数据库)
        /// </summary>
        /// <param name="whereLambda"></param>
        /// <returns></returns>
        System.Linq.IQueryable<T> GetIqueryableBy(Expression<Func<T, bool>> whereLambda);

        System.Linq.IQueryable<T> GetIqueryableBy_AsNoTracking(Expression<Func<T, bool>> whereLambda);

        #endregion

        Task<int> GetCountBy(Expression<Func<T, bool>> whereLambda);


        Task<int> ExcuteSql(string strSql, params object[] paras);

        DataTable SqlQueryForDataTatable(string strSql, params SqlParameter[] parameters);
    }

五、搭建Services层

image.png
作用:此层为业务逻辑层,负责将Repository仓储层的数据进行调用,至于如何是与数据库交互的,它不去管,这样就可以达到一定程度上的解耦,假如以后数据库要换,比如MySql,那Service层就完全不需要修改即可。

1、新建文件夹Base,在该文件夹下新建BaseServices.cs类

BaseServices.cs

public class BaseServices<T> where T : class, new()
    {
        protected IUnitOfWork unitOfWork;
        protected IBaseRepository<T> _baseDal; 

        /// <summary>
        /// 构造函数 
        /// </summary>
        /// <param name="unitOfWork">注入工作单元(所有的服务类对于数据库的操作都通过工作单元来,这样避免了每一个具体的服务都需要创建一个DBContext)</param>
        /// <param name="baseDal">注入服务基类</param>
        public BaseServices(IUnitOfWork unitOfWork, IBaseRepository<T> baseDal)
        {
            this.unitOfWork = unitOfWork;
            this._baseDal = baseDal;
        }
        public async Task<int> Add(T entity)
        {
            await _baseDal.Add(entity);
            return await unitOfWork.SaveChangesAsync();
        }

        public void AddAll_BulkInsertNew(List<T> models)
        {
            _baseDal.AddAll_BulkInsertNew(models);
        }
        public async Task<int> ModifyBy_GaoXiao(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda, System.Linq.Expressions.Expression<Func<T, T>> entity)
        {
            await _baseDal.ModifyBy_GaoXiao(whereLambda, entity);
            return await unitOfWork.SaveChangesAsync();
        }

        public async Task<int> DelBy_GaoXiao(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda)
        {
            await _baseDal.DelBy_GaoXiao(whereLambda);
            return await unitOfWork.SaveChangesAsync();
        }

        public async Task<int> ExcuteSql(string strSql, params object[] paras)
        {
            return await _baseDal.ExcuteSql(strSql, paras);

        }

        public async Task<int> GetCountBy(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda)
        {
            return await _baseDal.GetCountBy(whereLambda);
        }

        public async Task<T> GetFirstOrDefaultBy(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda)
        {
            return await _baseDal.GetFirstOrDefaultBy(whereLambda);
        }

        public IQueryable<T> GetIqueryableBy(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda)
        {
            return _baseDal.GetIqueryableBy(whereLambda);
        }

        public IQueryable<T> GetIqueryableBy_AsNoTracking(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda)
        {
            return _baseDal.GetIqueryableBy_AsNoTracking(whereLambda);
        }

        public async Task<T> GetLastOrDefaultBy(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda)
        {
            return await _baseDal.GetLastOrDefaultBy(whereLambda);
        }

        public async Task<Tuple<List<T>, int>> GetPagedList<S>(int pageSize, int pageIndex, System.Linq.Expressions.Expression<Func<T, bool>> whereLambda, System.Linq.Expressions.Expression<Func<T, S>> orderByLambda, bool isAsc)
        {
            return await _baseDal.GetPagedList(pageSize, pageIndex, whereLambda, orderByLambda, isAsc);
        }

        public async Task<bool> IsExist(System.Linq.Expressions.Expression<Func<T, bool>> whereLambda)
        {
            return await _baseDal.IsExist(whereLambda);
        }

        public DataTable SqlQueryForDataTatable(string strSql, params SqlParameter[] parameters)
        {
            return _baseDal.SqlQueryForDataTatable(strSql, parameters);
        }
    }

写在最后:

至此我们的仓储模式就已经搭建完成啦,该文仅简单的介绍了如何搭建仓储模式,引入EF Core。目前我们仅仅迈出了一小步,我们的实体类还没有创建相对应的仓储和服务,下一篇我们将使用T4模板将整个项目的仓储层和服务层完成半自动化搭建!