阅读本文你的收获
- 了解仓储模式及泛型仓储的优点
- 学会封装泛型仓储的一般设计思路
- 学习在ASP.NET Core WebAPI项目中使用EntityFrameworkCore.Data.Repository
本文中的案例是微软EntityFrameworkCore的一个仓储模式实现,这个仓储库不是我自己写的,而是使用了一个老外写的EntityFrameworkCore.Data.Repository,大家可以一起来学习一下,如果你觉得合适,可以直接用在你的项目中,很方便。案例代码下载
一、 什么是仓储模式
仓储(Repository)模式自2004年首次作为领域驱动模型DDD设计的一部分引入,仓储本质上是提供数据的抽象,以便应用程序可以使用具有接口的相似的简单抽象集合。从此集合中CURD是通过一系列直接的方法完成,无需处理连接、命令等问题,使用此种模式可帮助实现松耦合,并保持领域对象的持久性无知。
- 仓储模式是为了在程序的数据访问层和业务逻辑层之间创建的一个抽象层
- 仓储模式是一种数据访问模式,提供一种更松散耦合的数据访问方法
- 将创建数据访问的逻辑写在单独的类中即仓储
- 仓储负责和业务层进行持久化通信
下图为控制器和仓储协同工作的图例:

二、泛型仓储
仓储(Repository)是存在于工作单元和数据库之间单独分离出来的一层,是对数据访问的封装。其优点是
- 业务层无需知道具体实现,达到分离关注点;
- 提高对数据库访问的维护,对于仓储的改变,并不改变业务的逻辑;
如果我们采用的ORM框架是EF Core,实现仓储模式的话,那么类图设计一般如下:
通常实现仓储的时候,会使用泛型技术封装增删改查的的通用功能,类型T即为具体要进行增删改查处理的实体类型。
使用泛型仓储(Generic Repository)的好处有以下几点:
- 更好的可重用性:泛型仓储可以在多个实体类型之间共享和重用,减少了重复的代码编写和维护工作。
- 更好的类型安全性:使用泛型仓储可以在编译时就约束数据的类型,减少了运行时类型错误的可能性。
- 更好的简洁性:泛型仓储可以通过使用通用的方法和接口来简化数据访问的逻辑,提供统一的CRUD(增删改查)操作接口。
- 更好的可测试性:泛型仓储使得数据访问逻辑可以更容易地进行单元测试,因为可以使用模拟或者假数据来代替实际的数据存储。
- 更好的扩展性:泛型仓储可以通过继承或者接口实现来扩展其功能,例如添加自定义的查询方法或者过滤器。
三、基于EF Core实现的泛型仓储案例
开发环境:
操作系统: Windows 10 专业版
平台版本是:.NET 6
开发框架:ASP.NET Core WebApi、Entity Framework Core
开发工具:Visual Studio 2022
数据库: MySQL 5.7+
安装NuGet包
使用的NuGet包主要有:
- EntityFrameworkCore.Data.Repository – 一个老外封装好的开源EfCore仓储实现
- EntityFrameworkCore.Data.UnitOfWork-- 跟以上配套的工作单元
- Pomelo.EntityFrameworkCore.MySql – MySQL数据库提供程序
简单剖析一下 EntityFrameworkCore.Data.Repository
可以看到,作者定义了泛型仓储接口如下:
public interface IRepository<T> : IRepository, IDisposable, ISyncRepository<T>, ISyncRepository, IQueryFactory<T>, IAsyncRepository<T>, IAsyncRepository where T : class
{
}
IRepository< T >接口分别集继承了 ISyncRepository< T > 和 IAsyncRepository< T >这两个接口,分别是同步仓储方法接口和异步仓储方法接口。
//同步仓储方法接口
public interface ISyncRepository<T> : ISyncRepository, IRepository, IDisposable, IQueryFactory<T> where T : class
{
IList<T> Search(IQuery<T> query);
IList<TResult> Search<TResult>(IQuery<T, TResult> query);
T SingleOrDefault(IQuery<T> query);
TResult SingleOrDefault<TResult>(IQuery<T, TResult> query);
T FirstOrDefault(IQuery<T> query);
TResult FirstOrDefault<TResult>(IQuery<T, TResult> query);
T LastOrDefault(IQuery<T> query);
TResult LastOrDefault<TResult>(IQuery<T, TResult> query);
bool Any(Expression<Func<T, bool>> predicate = null);
int Count(Expression<Func<T, bool>> predicate = null);
long LongCount(Expression<Func<T, bool>> predicate = null);
TResult Max<TResult>(Expression<Func<T, TResult>> selector, Expression<Func<T, bool>> predicate = null);
TResult Min<TResult>(Expression<Func<T, TResult>> selector, Expression<Func<T, bool>> predicate = null);
decimal Average(Expression<Func<T, decimal>> selector, Expression<Func<T, bool>> predicate = null);
decimal Sum(Expression<Func<T, decimal>> selector, Expression<Func<T, bool>> predicate = null);
T Attach(T entity);
void AttachRange(IEnumerable<T> entities);
T Add(T entity);
void AddRange(IEnumerable<T> entities);
T Update(T entity, params Expression<Func<T, object>>[] properties);
int Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> expression);
void UpdateRange(IEnumerable<T> entities, params Expression<Func<T, object>>[] properties);
T Remove(T entity);
int Remove(Expression<Func<T, bool>> predicate);
void RemoveRange(IEnumerable<T> entities);
int ExecuteSqlCommand(string sql, params object[] parameters);
IList<T> FromSql(string sql, params object[] parameters);
void ChangeTable(string table);
void ChangeState(T entity, EntityState state);
EntityState GetState(T entity);
void Reload(T entity);
void TrackGraph(T rootEntity, Action<EntityEntryGraphNode> callback);
void TrackGraph<TState>(T rootEntity, TState state, Func<EntityEntryGraphNode<TState>

本文介绍了仓储模式在ASP.NETCore中的应用,重点讲解了如何使用泛型仓储封装EntityFrameworkCore的CRUD操作,以及在WebAPI项目中如何配置和使用EntityFrameworkCore.Data.Repository。

1672

被折叠的 条评论
为什么被折叠?



