我目前有一些空闲时间,所以我正在努力了解 DI 和 IoC 容器。我选择 unity 没有任何理由,除了我可以告诉我的主要框架之间没有重大差异,我应该太担心开始。随着事情变得越来越复杂,我意识到我可能需要做出改变,但现在我希望它能做到。
所以,我正在处理一个相对简单的数据访问场景,并实现了以下接口(interface)和数据访问类。
public interface IEventRepository
{
IEnumerable<Event> GetAll();
}
public class EventRepository : IEventRepository
{
public IEnumerable<Event> GetAll()
{
// Data access code here
}
}
然后使用我可以执行以下操作。
IUnityContainer container = new UnityContainer();
container.RegisterType(typeof(IEventRepository), typeof(EventRepository));
var eventRepo = container.Resolve<IEventRepository>();
eventRepo.GetAll();
如果我需要在 6 个月内根据我的理解更改我的数据库提供程序,我会创建一个新的 IEventRepository 实现并更新类型注册,这很好。
现在,这就是我感到困惑的地方。例如,如果我想实现一些缓存,我可以从 IEventRepository 的适当实现继承并覆盖适当的方法来实现必要的缓存。但是,这样做会使使用通过 DI 传入的 Moq 实现测试缓存是否正常工作变得更加困难,因此本着 DI 的真正精神,我认为创建 IEventRepository 的实现,然后使用 DI 请求一个像这样的 IEventRepository 的实际数据访问实现。
public class CachedEventRepository : IEventRepository
{
private readonly IEventRepository _eventRepo;
public CachedEventRepository(IEventRepository eventRepo)
{
if (eventRepo is CachedEventRepository)
throw new ArgumentException("Cannot pass a CachedEventRepository to a CachedEventRepository");
_eventRepo = eventRepo;
}
public IEnumerable<Event> GetAll()
{
// Appropriate caching code ultimately calling _eventRepo.GetAll() if needed
}
}
这是有道理的还是我做错了?你有什么建议?如果我做得正确,我该如何解决以下情况,以便 CachedEventRepository 获得 IEventRepository 的适当数据访问实现?
IUnityContainer container = new UnityContainer();
container.RegisterType(typeof(IEventRepository), typeof(EventRepository));
container.RegisterType(typeof(IEventRepository), typeof(CachedEventRepository));
var eventRepo = container.Resolve<IEventRepository>();
eventRepo.GetAll();
非常感谢您的帮助。
编辑 1 以下是我希望能够执行的最小起订量测试,我认为使用继承不可能实现,并且需要 DI。
var cacheProvider = new MemoryCaching();
var eventRepo = new Mock<IEventRepository>(MockBehavior.Strict);
eventRepo
.Setup(x => x.GetAll())
.Returns(() =>
{
return new Event[] {
new Event() { Id = 1},
new Event() { Id = 2}
};
});
var cachedEventRepo = new CachedEventRepository(
eventRepo.Object,
cacheProvider);
var data = cachedEventRepo.GetAll();
data = cachedEventRepo.GetAll();
data = cachedEventRepo.GetAll();
Assert.IsTrue(data.Count() > 0);
eventRepo.Verify(x => x.GetAll(), Times.Once());
// This set method should expire the cache so next time get all is requested it should
// load from the database again
cachedEventRepo.SomeSetMethod();
data = cachedEventRepo.GetAll();
data = cachedEventRepo.GetAll();
Assert.IsTrue(data.Count() > 0);
eventRepo.Verify(x => x.GetAll(), Times.Exactly(2));
最佳答案
好的,在对这个主题进行了一些思考并研究了 Unity 之后,我想到了这个。
public class EventRepository : IEventRepository
{
private readonly IDbManager _dbManager;
public EventRepository(IDbManager dbManager)
{
_dbManager = dbManager;
}
public virtual IEnumerable<Event> GetAll()
{
// Data access code
}
}
public class CachedEventRepository : IEventRepository
{
private readonly ICacheProvider _cacheProvider;
private readonly IEventRepository _eventRepo;
public ICacheProvider CacheProvider
{
get { return _cacheProvider; }
}
public CachedEventRepository(IEventRepository eventRepo, ICacheProvider cacheProvider)
{
if(eventRepo is CachedEventRepository)
throw new ArgumentException("eventRepo cannot be of type CachedEventRepository", "eventRepo");
_cacheProvider = cacheProvider;
_eventRepo = eventRepo;
}
public IEnumerable<Event> GetAll()
{
// Caching logic for this method with a call to _eventRepo.GetAll() if required
}
}
这需要下面的unity注册。对 IEventRepository 的解析请求将返回一个 CachedEventRepository。如果我想快速删除缓存,我只需删除 CachedEventRepository 注册,它将恢复为 EventRepository。
IUnityContainer container = new UnityContainer();
container.RegisterType<IDbManager, SqlDbManager>();
container.RegisterType<ICacheProvider, MemoryCaching>();
container.RegisterType<IEventRepository, EventRepository>();
container.RegisterType<IEventRepository, CachedEventRepository>(
new InjectionConstructor(
new ResolvedParameter<EventRepository>(),
new ResolvedParameter<ICacheProvider>())
);
然后这将允许我进行的测试。
一个简单的数据访问测试...SQL 是否工作
IUnityContainer container = new UnityContainer();
container.RegisterType<IDbManager, SqlDbManager>();
container.RegisterType<EventRepository>();
var repo = container.Resolve<EventRepository>();
var data = repo.GetAll();
Assert.IsTrue(data.Count() > 0);
一个简单的缓存测试...缓存系统是否工作
var cache = new MemoryCaching();
var getVal = cache.Get<Int32>(
"TestKey",
() => { return 2; },
DateTime.UtcNow.AddMinutes(5));
Assert.AreEqual(2, getVal);
getVal = cache.Get<Int32>(
"TestKey",
() => { throw new Exception("This should not be called as the value should be cached"); },
DateTime.UtcNow.AddMinutes(5));
Assert.AreEqual(2, getVal);
以及对两者协同工作的测试......在各个方法上的缓存是否按预期工作。缓存是否在应该过期的时候过期,方法参数是否正确工作以触发新的数据库请求等。
var cacheProvider = new MemoryCaching();
var eventRepo = new Mock<IEventRepository>(MockBehavior.Strict);
eventRepo
.Setup(x => x.GetAll())
.Returns(() =>
{
return new Event[] {
new Event() { Id = 1},
new Event() { Id = 2}
};
});
var cachedEventRepo = new CachedEventRepository(
eventRepo.Object,
cacheProvider);
cachedEventRepo.CacheProvider.Clear();
var data = cachedEventRepo.GetAll();
data = cachedEventRepo.GetAll();
data = cachedEventRepo.GetAll();
Assert.IsTrue(data.Count() > 0);
eventRepo.Verify(x => x.GetAll(), Times.Once());
cachedEventRepo.SomeSetMethodWhichExpiresTheCache();
data = cachedEventRepo.GetAll();
data = cachedEventRepo.GetAll();
Assert.IsTrue(data.Count() > 0);
eventRepo.Verify(x => x.GetAll(), Times.Exactly(2));
你怎么看这个?我认为它提供了良好的分离和良好的可测试性。
关于c# - 对 DI、IoC、Unity 和 Moq 的类型注册和实现感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10231537/