写在前面:
一直都想自己从零搭建一个项目框架,来熟悉.net Core 与DDD框架,奈何技术不过关,这个计划一直被无限期的咽喉,最近下定决定要完成这件事情。正好运用最近的.NET 5与稍微简单一点的仓储模式来搭建一个完整的博客系统 …本文仅用作个人学习参考,如有不对之处,请批评指正!
一、引入EF Core+Code First
1.新增类库Model层
此层用于项目中的实体类,项目中所有有关实体类,均在此层进行添加
2.新增类库EntityFrameworkCore
项目结构:
EFDBContext:EF Core上下文基类
Migrations:Code First迁移文件夹,自动生成
EFDBContext.cs:
public class EFDBContext : DbContext
{
public EFDBContext(DbContextOptions<EFDBContext> options) : base(options)
{
}
public EFDBContext()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
#region Entity DbSest<> 注:新增一个实体类都需要在下面写上,否则不会迁移到数据库中
public virtual DbSet<SysUserInfo> SysUserInfo { get; set; }
public virtual DbSet<BlogArticle> BlogArticle { get; set; }
#endregion
}
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包管理器->程序包管理器控制台:
下面这一步很关键,程序包控制台中需要选中我们EFDBContext所在的项目,而解决方案中需要将我们配置连接字符串的项目作为启动项目
Add-Migrations (迁移名称):执行完成之后会自动生成迁移的类
Update-Database:此命令执行完成之后数据库就已经被同步更新啦
这一步完成之后,项目与数据库连接就已经完成啦,下面我们开始逐步搭建我们的仓储模式。
二、搭建IRepository层
作用:此层为仓储接口层,定义对数据操作的契约(命令执行、重载不同查询结果集合、查询实体、查询返回结果值)。
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层
作用:此层为一个管理数据持久层,主要实现仓储接口层,是具体对数据库操作的方法实现,同时对工作单元接口的实现,负责数据的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层
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层
作用:此层为业务逻辑层,负责将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模板将整个项目的仓储层和服务层完成半自动化搭建!