通过运行值使用简单注射器ABD WebFormsMVP同构造函数(Pass runtime valu

2019-07-20 16:13发布

我试图用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;
        }
    }
}

Answer 1:

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作为构造函数的参数,因为这将是非常可能的,你需要给其他注册加入到容器中,并且你不想注册的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 ,你看到很多缓存在那里往前走。 我不知道他们为什么这样做,但是从性能的角度来看,你不都需要这样的事情简单的注射器。 也许我失去了一些东西,但我不明白为什么应该有一个缓存。

而且更糟糕的,还有在并发错误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不可改变的,这意味着你创建原始字典的副本,添加新的价值,并更换参考老字典,新的。 然而,在这种情况下,我很可能只是锁定了整个事情。

这是题外话了一点点,但只是想告诉:-)



文章来源: Pass runtime value to constructor using Simple Injector abd WebFormsMVP