我有如下模型:
public interface IUserLookupService {
Guid[] GetDirectChilds(Guid userId);
Guid[] GetAllChilds(Guid userId);
Guid GetDepartment(Guid userId);
}
public interface ICanRefreshCache{
void Refresh();
}
public class XHandler : IEventHandler<UserNameChanged> { ... }
public class YHandler : IEventHandler<TeamCreated> { ... }
public class CachedUserLookupService
: IUserLookupService,
ICanRefreshCache,
IEventHandler<UserNameChanged>
{
private Func<ISession> _sessionFactory;
private IDictionary<Guid, UserInfo> _users = new Dictionary<Guid, UserInfo>();
public CachedUserLookupService(Func<ISession> sessionFactory) {
_sessionFactory = sessionFactory;
}
public void Handle(UserNameChanged ev) {
// change cache with new event parameters
}
public void Refresh() {
var session = _sessionFactory();
// get user from db then create userinfo...
}
public Guid[] GetDirectChilds(Guid userId) {
// code
}
public Guid[] GetAllChilds(Guid userId) {
// code
}
public Guid GetDepartment(Guid userId) {
// code
}
public class UserInfo
{
public Guid Id { get; set; }
public string FullName { get; set; }
public Guid? ParentId {get;set;}
}
}
public class CachedTeamService
: ITeamService,
ICanRefreshCache,
IEventHandler<TeamCreated>{
// similar with CachedUserLookupService
}
我的注册:
container.RegisterManyForOpenGeneric(typeof(IEventHandler<>),
(closedServiceType, implementations) =>
{
container.RegisterAll(closedServiceType, implementations);
},
applicationAssembly, typeof(Constants).Assembly);
var serviceRegistrations =
from type in applicationAssembly.GetExportedTypes()
where type.Name.EndsWith("Service")
where !type.IsAbstract
where !type.IsInterface
select new
{
Services = type.GetInterfaces(),
Implementation = type
};
var lifeStyles = new Dictionary<Type, Lifestyle>()
{
{typeof(CachedUserLookupService),Lifestyle.Singleton},
{typeof(CachedTeamService),Lifestyle.Singleton}
};
List<Type> cacheableComponents = new List<Type>();
foreach (var reg in serviceRegistrations)
{
foreach (var service in reg.Services)
{
Lifestyle lifeStyle;
if (lifeStyles.TryGetValue(reg.Implementation, out lifeStyle) == false)
{
lifeStyle = Lifestyle.Singleton;
}
if (typeof(ICanRefreshCache) == service)
{
cacheableComponents.Add(reg.Implementation);
continue;
}
container.Register(service, reg.Implementation, lifeStyle);
}
}
container.RegisterAll(typeof(ICanRefreshCache), cacheableComponents);
我想在系统启动时使用ICanRefreshCache-> Refresh方法刷新所有缓存,所以我调用:
步骤1:
container.GetAllInstances<ICanRefreshCache>().Each(c=>c.Refresh());
第2步:
在我调用IEventHandler<UserNameChanged>
或任何其他接口键入属于CachedUserLookupService(或CachedTeamService)的任何时间之后,返回的实例与步骤1实例不同,因此此注册无济于事。
我需要简单注入器注册才能提供以下调用。
// must return Singleton CachedUserLookupService + other
// IEventHandler<UserNameChanged> implementations
container.GetAllInstances<IEventHandler<UserNameChanged>>();
// must return Singleton CachedTeamService + other
// IEventHandler<TeamCreated> implementations
container.GetAllInstances<IEventHandler<TeamCreated>>();
// must return Singleton CachedUserLookupService
container.GetInstance<IUserLookupService>();
// must return Singleton CachedTeamService
container.GetInstance<ITeamService>();
// must return Singleton CachedUserLookupService + Singleton CachedTeamService
container.GetAllInstances<ICanRefreshCache>();
注意:此答案中的信息已过时。在Simple Injector v4中,情况发生了很大变化。
如果我理解正确,则您有一个实现多个接口的组件,并且您希望每个注册都映射到该组件的相同实例。所以,不管你是否解决了ITeamService
,ICanRefreshCache
或者IEventHandler<TeamCreated>
,你想要得到的同一个实例CachedTeamService
。
在Simple Injector中执行此操作的一般方法是Registration
手动创建一个实例,并为每个接口注册它,如下所示:
var registration =
Lifestyle.Singleton.CreateRegistration<CachedTeamService>(container);
container.AddRegistration(typeof(ITeamService), registration);
container.AddRegistration(typeof(ICanRefreshCache), registration);
container.AddRegistration(typeof(IEventHandler<TeamCreated>), registration);
这在这里解释。
但是,您的情况要复杂一些,因为您将批量注册RegisterManyForOpenGeneric
与常规注册一起使用。因此,您应该IEventHandler<TeamCreated>
以单例形式取消想要的批量注册,或者您需要完全替换注册。
但是,在这种情况下无法替换注册,因为Simple Injector不允许您替换集合中的注册。因此,我们可以按以下方式禁止该类型的注册:
Type[] typesToRegisterManually = new[]
{
typeof(CachedTeamService)
};
container.RegisterManyForOpenGeneric(typeof(IEventHandler<>),
(service, impls) =>
{
container.RegisterAll(service, impls.Except(typesToRegisterManually));
},
assemblies);
var registration =
Lifestyle.Singleton.CreateRegistration<CachedTeamService>(container);
container.AddRegistration(typeof(ITeamService), registration);
container.AddRegistration(typeof(ICanRefreshCache), registration);
// Using SimpleInjector.Advanced
container.AppendToCollection(typeof(IEventHandler<TeamCreated>), registration);
但是,我的经验是,复杂的注册通常表明您的代码中违反了SOLID原则。很难对您的设计提供任何具体的反馈,但是我发现具有这些多个接口的类很可能具有多种职责,并且有多种更改理由(它们违反了SRP),从而导致您在添加新功能时进行更改(这是违反OCP的行为)。
相反,我可以提供一些建议:
IEventHandler<T>
移到其自己的类中。事件处理程序显然是另一种责任,接口名称已经在告诉您这一点。然后,此类可以依赖于从中提取逻辑的旧类,也可以将事件处理程序所依赖的逻辑提取到其自己的类中,并使“旧”类和事件处理程序都依赖于此新的共享类。IUserLookupService
接口可以自主安排似乎是一种变相的资源库,这基本上意味着你违反了SRP,OCP和ISP(说明这篇文章)。因此,如该文章所述,自定义查询应具有自己的抽象:IQueryHandler<TQuery, TResult>
。这样可以很容易地将横切关注点应用到它们上,使其成为SOLID,并使用进行注册非常简单container.RegisterManyForOpenGeneric
。这给您留下的是在大多数情况下仅实现一种抽象的类,但类实现的情况除外ICanRefreshCache
。对于这种特殊情况,我建议进行复合 ICanRefreshCache
实现以允许触发ICanRefreshCache
应用程序中的所有操作。但是IEnumerable<ICanRefreshCache>
,与其将a注入到该复合物中,不应该将该复合物作为您的“合成根”的一部分,并使其依赖于容器。这使您可以在运行时搜索完整的配置以查找所有ICanRefreshCache
实现。
这样的组合可能看起来像这样:
public class CanRefreshCacheComposite : ICanRefreshCache
{
private readonly Lazy<InstanceProducer[]> canRefreshProducers;
public CanRefreshCacheComposite(Container container) {
this.canRefreshProducers =
new Lazy<InstanceProducer[]>(() => GetProducers(container).ToArray());
}
public void Refresh() {
foreach (var producer in this.canRefreshProducers.Value) {
var refresher = (ICanRefreshCache)producer.GetInstance();
refresher.Refresh();
}
}
private IEnumerable<InstanceProducer> GetProducers(Container container) {
return
from producer in container.GetCurrentRegistrations()
where typeof(ICanRefreshCache).IsAssignableFrom(
producer.Registration.ImplementationType)
select producer;
}
}
您可以按以下方式注册:
container.RegisterSingle<ICanRefreshCache, CanRefreshCacheComposite>();
// To make sure all all ICanRefreshCache implementations that are part of
// a collection are known to the container, call Verify() when you're done
// registering.
container.Verify();
这样,您可以简单地ICanRefreshCache
从代码内部进行依赖,在其Refresh
上调用方法,然后复合方法将完成其余工作。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句