所以,一些结论。我想我会写下来,以防其他人尝试一起使用/单元测试 EF、Windsor 和 MVC。
首先,由于 DbContext 实现了 Repository 和 Unit of Work 模式,您需要考虑这些实现是否会提供服务,或者您是否需要创建自己的实现。
我选择创建自己的存储库,遵循 DDD 模式:每个聚合根一个。原因:封装查询代码,防止泄露到应用层,测试应用控制器时更容易mock。我创建了一个基于IRepository<TEntity>
. 那里有很多例子。我发现这是一个很好的:http ://architects.dzone.com/articles/implementing-repository
另一方面,我决定放弃 IUnitOfWork 服务,而选择默认实现。但是,我创建了一个 IDbContext 抽象(不知道为什么微软自己不这样做),以便在测试 Repository 服务时可以模拟 DbContext。
我只给了 IDbContext 我想在存储库中使用的 DbContext 成员。所以:
public interface IDbContext: IDisposable
{
Database Database { get; }
DbEntityEntry Entry(object entity);
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
int SaveChanges();
}
然后,我为我的 IDbContext 和 IRepository 服务创建了 Windsor 工具和安装程序:
public class EntityFrameworkFacility: AbstractFacility
{
protected override void Init()
{
Kernel.Register(Component.For<IDbContext>()
.ImplementedBy<MyEntities>()
.LifestylePerWebRequest(),
Component.For(typeof(IRepository<>))
.ImplementedBy(typeof(Repository<>))
.LifestylePerWebRequest());
}
}
public class PersistenceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<EntityFrameworkFacility>();
}
}
最后一步是扩展实体框架上下文类以实现 IDbContext,并隐藏 Set() 方法以返回 IDbSet 而不是 DbSet:
public partial class MyEntities : IDbContext
{
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
}
有了所有这些(以及 Windsor 文档中说明的 ControllerFactory 注册),让 Windsor 根据需要将 IRepository 对象(或 IDbContext)注入控制器构造函数变得微不足道:
public ControllerBase(IRepository<Contact> repo)
{
_repo = repo;
}
在存储库单元测试中,可以使用模拟 IDbContext 支持真实的存储库实例:
mocks = new MockRepository();
context = mocks.StrictMock<IDbContext>();
repo = new Repository<Contact>(context);
在 Controller 单元测试中,可以使用模拟存储库:
mocks = new MockRepository();
repo = mocks.StrictMock<IRepository<Contact>>();
ContactController controller = new ContactController(repo);