我试图用SimpleInjector结合WebFormsMvp。
为了方便DI WebFormsMvp提供IPresenterFactory
接口。
它包含了Create
方法,该方法提供了演示型解决和视图实例 。
我需要的视图实例 注入 到 主持人 的构造 。
主讲人也有需要由容器创建其他依赖关系 。
这是我走到这一步,但它并不理想。
什么是该问题的正确的解决方案 ?
主持人构造函数:
public FooPresenter(IFooView view, IClientFactory clientFactory) : base(view)
厂:
public class SimpleInjectorPresenterFactory : IPresenterFactory
{
private readonly Container _container;
private IView _currentView;
public SimpleInjectorPresenterFactory()
{
_container = new Container();
Func<Type, bool> isIView =
type => typeof(IView).IsAssignableFrom(type);
_container.ResolveUnregisteredType += (s, e) => {
if (isIView(e.UnregisteredServiceType))
e.Register(() => _currentView);
};
}
public IPresenter Create(Type presenterType, Type viewType, IView viewInstance)
{
lock (_currentView)
{
_currentView = viewInstance;
return _container.GetInstance(presenterType) as IPresenter;
}
}
}
WebFormsMvp迫使你采取在主持人的构造函数的观点,但是这会触发循环引用。 如果你在工厂实现了不同的容器来看看,你会发现每个他们做了不同的技巧来解决这个怪癖设计容器。 例如,对于团结,他们创建一个子容器并注册在子容器这一观点,并使用该子容器解决了主持人。 很离奇和性能沉重。
而不是采取视图中演示的构造,WebFormsMvp的设计者应该已经取得了查看上写属性IPresenter
接口。 这将使尴尬方便地设置在主持人的看法。 事情是这样的:
public IPresenter Create(Type presenterType, IView view)
{
var presenter = (IPresenter)_container.GetInstance(presenterType);
presenter.View = view;
return presenter;
}
遗憾的是他们并没有这样做,这是不可能的扩展设计,让这个(没有做使用反射真的讨厌的东西)。
简单的喷油器不支持提供构造函数参数到GetInstance()
方法。 有很好的理由,因为这通常会导致服务定位器反模式,你可以随时去解决这个通过改变设计。 在你的情况,你没有做那个古怪的设计,所以你不能改变它。
你所做的与ResolveUnregisteredType
是相当聪明。 我也不会想到这个问题我自己。 而且因为我的背后简单喷油器的领先开发,我在的位置说,你做了什么是真正聪明的:-)
就在两个点的约您的反馈SimpleInjectorPresenterFactory
。
首先,你应该提供Container
作为构造函数的参数,因为这将是非常可能的,你需要给其他注册加入到容器中,并且你不想注册的Container
内SimpleInjectorPresenterFactory
。
其次,可以通过使用改进的代码System.Threading.ThreadLocal<IView>
这可以让你摆脱全球锁。 锁防止他人同时进行的任何演讲,这可能您的网站慢下来。
所以这里有一个重构版本:
public class SimpleInjectorPresenterFactory : IPresenterFactory {
private readonly Container _container;
private ThreadLocal<IView> _currentView = new ThreadLocal<IView>();
public SimpleInjectorPresenterFactory(Container container) {
_container = container;
_container.ResolveUnregisteredType += (s, e) => {
if (typeof(IView).IsAssignableFrom(e.UnregisteredServiceType)) {
e.Register(() => _currentView.Value);
}
};
}
public IPresenter Create(Type presenterType, Type viewType,
IView viewInstance)
{
_currentView.Value = viewInstance;
try {
return _container.GetInstance(presenterType) as IPresenter;
} finally {
// Clear the thread-local value to ensure
// views can be disposed after the request ends.
_currentView.Value = null;
}
}
}
如果你看的实施UnityPresenterFactory
,你看到很多缓存在那里往前走。 我不知道他们为什么这样做,但是从性能的角度来看,你不都需要这样的事情简单的注射器。 也许我失去了一些东西,但我不明白为什么应该有一个缓存。
而且更糟糕的,还有在并发错误UnityPresenterFactory
。 看看这个方法:
private Type FindPresenterDescribedViewTypeCached(Type presenter,
IView view)
{
IntPtr handle = presenter.TypeHandle.Value;
if (!this.cache.ContainsKey(handle))
{
lock (this.syncLock)
{
if (!this.cache.ContainsKey(handle))
{
Type viewType = CreateType(presenter, view);
this.cache[handle] = viewType;
return viewType;
}
}
}
return this.cache[handle];
}
乍一看,这个代码看起来不错,因为双重检查锁来实现。 不幸的是,高速缓存(字典)从锁外面读,而它是在锁内更新。 这不是线程安全的。 相反,开发商应该要么包裹在一个锁定整个事情,使用ConcurrentDictionary
(.NET 4只),或考虑cache
不可改变的,这意味着你创建原始字典的副本,添加新的价值,并更换参考老字典,新的。 然而,在这种情况下,我很可能只是锁定了整个事情。
这是题外话了一点点,但只是想告诉:-)