使用 UnitOfWork 模式和 EntityFramework 的非常薄的数据层






4.83/5 (13投票s)
在使用 Unit of Work 模式和 Entityframework 时构建非常薄的数据层
引言
在我作为软件开发人员的职业生涯中,我参与过许多项目,并看到了使用不同模式访问数据库的不同方式,有时为了完成相同的事情却需要编写过多的代码。
在使用 EntityFramework 时,我们开发者应该意识到 DB 上下文是“神圣的”,应该小心处理。在需要时创建一个,并在完成后立即释放它。
不要在不同的线程之间共享它,也不要在整个应用程序实例生命周期内保持它的存活,否则你可能会遇到内存泄漏。
下面,你将找到使用 EntityFramework
和 DContext
实现 UnitOfWork
模式的代码。
背景
如果你正在使用服务/仓库模式,并且想要构建一个使用工作单元模式的精简仓库,那么这应该能让你走上正确的道路。
我见过不同的方式来暴露仓库中的数据,有时直接从仓库返回 IQueryable
,我不推荐这种方式,原因有三个:
- 在需要编写测试时,它更难模拟。
- 每次从数据库读取数据时,都需要使用
ToList()
方法将其转换为 CLR 类型。 - 组合查询会影响性能(阅读微软的这个链接)。
Using the Code
这一次,我们将构建一个简单的仓库。 很多人认为所有处理数据的事情都必须通过仓库来完成(我见过一个使用仓库访问 Web 服务的项目),我认为仓库应该只处理对数据库的查询。
下面是设置使用 EntityFramework
的工作单元模式的接口和类。
这个接口定义了 Repository
public interface IGenericRepository
{
Task<IEnumerable<T>> FindAsync<T>(Expression<Func<T, bool>> expression) where T : class;
Task<T> SingleOrDefaultAsync<T>(Expression<Func<T, bool>> expression) where T : class;
void Add<T>(T entity) where T : class;
void Update<T>(T entity) where T : class;
void Delete<T>(T entity) where T : class;
}
这个类实现了 IGenericRepository
public class GenericRepository : IGenericRepository
{
private readonly IDatabaseContext _dbContext;
public GenericRepository(IDatabaseContext dbContext)
{
_dbContext = dbContext;
}
public async Task<IEnumerable<T>> FindAsync<T>
(Expression<Func<T, bool>> expression) where T : class
{
return await _dbContext.Set<T>().Where(expression).ToListAsync();
}
public async Task<T> SingleOrDefaultAsync<T>
(Expression<Func<T, bool>> expression) where T : class
{
return await _dbContext.Set<T>().SingleOrDefaultAsync(expression);
}
public void Add<T>(T entity) where T : class
{
_dbContext.Set<T>().Add(entity);
}
public void Update<T>(T entity) where T : class
{
_dbContext.Entry(entity).State = EntityState.Modified;
}
public void Delete<T>(T entity) where T : class
{
_dbContext.Set<T>().Remove(entity);
}
}
工作单元
使用工作单元模式可以帮助你对数据库模型进行多次更改并一次性提交所有更改,这样可以确保所有更改都已正确保存,或者在其他情况下,你可以回滚。
定义工作单元接口
public interface IUnitOfWork : IDisposable
{
IGenericRepository Repository();
Task CommitAsync();
}
实现 IUnitOfWork
的类,你可以看到我每个 UnitOfWork
实例使用一个 DB 上下文。
public class UnitOfWork : IUnitOfWork
{
private readonly IDatabaseContext _databaseContext;
public UnitOfWork(IDatabaseContext databaseContext)
{
_databaseContext = databaseContext;
}
public IGenericRepository Repository()
{
return new GenericRepository(_databaseContext);
}
public void Dispose()
{
_databaseContext.Dispose();
}
public Task CommitAsync()
{
return _databaseContext.SaveChangesAsync();
}
}
对于每次需要时创建一个新的 DB 上下文,我建议使用工厂模式。
public interface IUnitOfWorkFactory
{
IUnitOfWork Create();
}
实现 IUnitOfWorkFactory
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
public IUnitOfWork Create()
{
return new UnitOfWork(new DatabaseContext());
}
}
建议定义一个接口来暴露 DB 上下文的属性。
public interface IDatabaseContext : IDisposable
{
DbEntityEntry Entry(object entity);
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbSet Set(System.Type entityType);
int SaveChanges();
Task<int> SaveChangesAsync();
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}
public class DatabaseContext : DbContext, IDatabaseContext
{
public DatabaseContext() : base("OrganizationDb")
{
Database.SetInitializer<DatabaseContext>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Add your model configuration here
}
}
现在,我们能够使用我们的 DB 模型来访问数据库并读取数据。
下面,你将找到一个示例服务。
public interface IPersonService
{
Task<List<PersonDto>> GetPersonsAsync(string term);
}
public class PersonService : IPersonService
{
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public PersonService(IUnitOfWorkFactory unitOfWorkFactory)
{
_unitOfWorkFactory = unitOfWorkFactory;
}
public async Task<List<PersonDto>> GetPersonsAsync(string term)
{
using (var unitOfWork = _unitOfWorkFactory.Create())
{
var persons = await unitOfWork.Repository().FindAsync<Person>
(x => x.Name.Contains(term));
return persons.Select(person => new PersonDto(person.Name)).ToList();
}
}
}
这只是一个简单的示例,你可以随时扩展仓库实现,或者配置你的 IoC 在每次请求后调用 Dispose
。天空是极限 :)
祝您编码愉快!