仓储和工作单元模式
仓储模式
为什么要用仓储模式
通常不建议在业务逻辑层直接访问数据库。因为这样可能会导致如下结果:
重复的代码
编程错误的可能性更高
业务数据的弱类型
更难集中处理数据,比如缓存
无法轻松地从外部依赖项测试业务逻辑
在业务逻辑层通过仓库模式访问数据则可以实现如下特点:
最大化可以用自动化测试的代码量,并隔离数据层以支持单元测试。
对数据集中管理、提供一致的访问规则和逻辑。
通过将业务逻辑与数据或服务访问逻辑分隔开,从而提高代码的可维护性和可读性。
使用强类型的Entity以便在编译时识别问题而不是在运行时
实现仓储模式
使用仓储模式是为了分离业务层和数据源层,并实现业务层的Model和数据源层的Model映射。(ViewModel和Entity之间的映射)。即业务逻辑层应该和数据源层无关,业务层只关心结果,数据源层关心细节。
数据源层和业务层之间的分离有三个好处:
集中了数据逻辑或Web服务访问逻辑。
为单元测试提供了一个替代点。
提供了一种灵活的体系结构,可以作为应用程序的整体设计进行调整。
一、定义仓储接口
所有的仓储要实现该接口。该接口定义了对数据的基本操作。
public interface IRepository where TEntity : class
{
#region 属性
//IQueryable Entities { get; }
#endregion
#region 公共方法
void Insert(TEntity entity);
void Insert(IEnumerable entities);
void Delete(object id);
void Delete(TEntity entity);
void Delete(IEnumerable entities);
void Update(TEntity entity);
TEntity GetByKey(object key);
#endregion
}
二、实现泛型仓储基类
该类为仓储的泛型基类,实现之前定义的仓储接口(IRepository),并包含数据上下文(DbContext),数据集(DataSet)。
每个表都会对应一个实体(Entity)。每个实体(Entity)对应一个仓储。把实体作为泛型仓储基类的参数,来实现每个实体对应的仓储。
(使用泛型仓储基类可以把实体作为泛型参数来创建对应的仓储。)
//泛型仓储基类
public class EFBaseRepository : IRepository where TEntity : class
{
//数据上下文
internal DbContext context;
//数据集
internal DbSet dbSet;
public EFBaseRepository(DbContext context)
{
this.context = context;
this.dbSet = context.Set();
}
//public IQueryable Entities => context.Set();
public void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public void Delete(IEnumerable entities)
{
dbSet.RemoveRange(entities);
}
public void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public TEntity GetByKey(object key)
{
return dbSet.Find(key);
}
public void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public void Insert(IEnumerable entities)
{
dbSet.AddRange(entities);
}
public void Update(TEntity entity)
{
dbSet.Attach(entity);
context.Entry(entity