EfUnitOfWork 继承 DbContext 和 IUnitOfWork,使用 EF 作为对象管理和持久化,这样实现好像没有什么问题,我们一般也是这样做的,其实,这是一个坑,我们后面会讲到,另外,之前说到 EF 中有 SaveChanges,为什么还有包裹一层 IUnitOfWork?其实就是说的上面代码,因为所有的 IUnitOfWork 的实现,我们都是使用的 EF,既然如此,为啥不把 IUnitOfWork 这个空壳拿掉呢?有人会说了,IUnitOfWork 是 DDD 中的概念,巴拉巴拉,不能拿掉,要不然就不是 DDD 了呢?
上面的问题先放在这个,如果 EfUnitOfWork 这样实现 IUnitOfWork,那 Repository 会怎样?看下面的代码:
public class StudentRepository: IStudentRepository { private IQueryable<Student> _students; public StudentRepository(IUnitOfWork unitOfWork) { _students = unitOfWork.Set<Student>();//没有了之前的 Set<TEntity>(),咋办啊??? } public Student Get(int id) { return _students.Where(x => x.Id == id).FirstOrDefault(); } //.... }代码进行到这,突然进行不下去了,咋办呢?如果你回过头去修改 IUnitOfWork 的接口,比如增加 Set<TEntity>(),这时候将又回到开始的问题,一切将前功尽弃,这么解决呢?这时候要停下来,思考 Repository 和 IUnitOfWork 的关系,也就是之前提到的,它们俩有关系么???
IUnitOfWork 的定义上面说过了,我们再看下 Repository 的定义:
重点在于访问,Repository 是用来访问领域对象的,所以,之前我们在 IRepository 中定义 Add、Update、Renove 等等接口,我觉得这些不是很恰当,因为对象列表的更改,我们可以用 IUnitOfWork 记录和实现,Repository 和 IUnitOfWork 应该是平级的概念,如果在 Repository 中去使用 IUnitOfWork,就有点违背其定义了。
IUnitOfWork 有其 EfUnitOfWork 的实现,难道我们还要搞一个 IRepository 对应的 EfRepository 实现?很显然,如果这样设计是非常冗余的,这时候,你是不是想到了我们还没有提到的 IDbContext 呢???没错就是它,让 UnitOfWork 和 Repository 都依赖于 IDbContext,而不是依赖于具体的 EF,这样也就没有了之前一直提到的 UnitOfWork 和 EF 的问题,具体怎么做呢?我们得先定义 IDbContext:
public interface IDbContext { DbSet<TEntity> Set<TEntity>() where TEntity : class; DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class; int SaveChanges(); }IDbContext 的作用,就是提供对象列表的一切操作接口,接下来实现一个 SchoolDbContext:
public class SchoolDbContext : DbContext, IDbContext { public SchoolDbContext() : base("name=db_school") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Student>(); modelBuilder.Entity<Teacher>(); base.OnModelCreating(modelBuilder); } }SchoolDbContext 有点像我们之前的 EfUnitOfWork,但它和 IUnitOfWork 没有任何关系,它的作用就是提供具体的对象持久化和访问,我们一般会放在 Infrastructure 层。另外,可以看到 SchoolDbContext 似乎并没有实现 IDbContext 的接口,为什么呢?因为我们继承了 DbContext,这些接口都在 DbContext 中进行进行实现了,你可以按 F12 进行查看。
接下来我们要对 EfUnitOfWork 进行改造了,IUnitOfWork 和 EF 已经没有半毛钱关系了,所以我们命名直接去掉 Ef,具体实现代码:
public class UnitOfWork : IUnitOfWork { private IDbContext _dbContext; public UnitOfWork(IDbContext dbContext) { _dbContext = dbContext; } public void RegisterNew<TEntity>(TEntity entity) where TEntity : class { _dbContext.Set<TEntity>().Add(entity); } public void RegisterDirty<TEntity>(TEntity entity) where TEntity : class { _dbContext.Entry<TEntity>(entity).State = EntityState.Modified; } public void RegisterClean<TEntity>(TEntity entity) where TEntity : class { _dbContext.Entry<TEntity>(entity).State = EntityState.Unchanged; } public void RegisterDeleted<TEntity>(TEntity entity) where TEntity : class { _dbContext.Set<TEntity>().Remove(entity); } public bool Commit() { return _dbContext.SaveChanges() > 0; } public void Rollback() { throw new NotImplementedException(); } }UnitOfWork 脱离了 EF, 是不是有种小清新的感觉?UnitOfWork 依赖于 IDbContext,所以,UnitOfWork 并不关心用哪种具体技术进行实现,你只需要给我对象访问和持久化接口就可以了,下面再看一下之前进行不下去的 Repository:
public class StudentRepository: IStudentRepository { private IQueryable<Student> _students; public StudentRepository(IDbContext dbContext) { _students = dbContext.Set<Student>(); } public Student Get(int id) { return _students.Where(x => x.Id == id).FirstOrDefault(); } }