gpt4 book ai didi

c# - 使用简单注入(inject)器和 WebForms MVP 将运行时值传递给构造函数

转载 作者:太空狗 更新时间:2023-10-29 21:40:13 25 4
gpt4 key购买 nike

我正在尝试将 SimpleInjector 与 WebFormsMvp 结合

为了方便 DI WebFormsMvp 提供了 IPresenterFactory 接口(interface)。
它包含 Create 方法,该方法提供了要解析的presenter 类型 View 实例
我需要将 View 实例注入(inject) presenterconstructor
演示者具有需要由容器创建的其他依赖项

这是我目前得到的结果,但并不理想。
该问题的正确解决方案是什么?

Presenter 构造函数:

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 强制您在演示者的构造函数中获取 View ,但这会触发循环引用。如果您查看不同容器的工厂实现,您会发现对于每个容器,它们都采用了不同的技巧来解决设计中的这个怪癖。例如,通过统一,他们创建一个子容器并在子容器中注册该 View ,并使用该子容器解析演示者。非常奇怪并且性能很重。

WebFormsMvp 的设计者应该让 View 成为 IPresenter 上的可写属性,而不是在演示者的构造函数中获取 View 。界面。这将使在演示者上设置 View 变得非常容易。像这样:

public IPresenter Create(Type presenterType, IView view)
{
var presenter = (IPresenter)_container.GetInstance(presenterType);
presenter.View = view;
return presenter;
}

不幸的是,他们没有这样做,并且不可能扩展设计以允许这样做(如果不使用反射做一些非常讨厌的事情)。

简单注入(inject)器不支持向 GetInstance() 提供构造函数参数方法。有充分的理由,因为这通常会导致服务定位器反模式,并且您始终可以通过更改设计来解决这个问题。就您的情况而言,您并没有做出那种古怪的设计,因此您无法更改它。

您对 ResolveUnregisteredType 做了什么很聪明。我自己是不会考虑这个的。由于我是 Simple Injector 背后的首席开发人员,我可以说您所做的真的很聪明 :-)

关于您的 SimpleInjectorPresenterFactory 的两点反馈.

首先,您应该提供 Container作为构造函数参数,因为您很可能需要向容器添加其他注册,并且您不想注册 ContainerSimpleInjectorPresenterFactory里面.

其次,您可以使用 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 的实现,你会看到里面有很多缓存。我不知道他们为什么这样做,但从性能的角度来看,你根本不需要 Simple Injector 这样的东西。也许我遗漏了什么,但我不明白为什么应该有缓存。

但更糟糕的是,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不可变的,这意味着您创建原始字典的副本,添加新值,并将对旧字典的引用替换为新字典。但是,在这种情况下,我可能会锁定整个内容。

这有点离题,但只是想告诉你 :-)

关于c# - 使用简单注入(inject)器和 WebForms MVP 将运行时值传递给构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15123515/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com