传递运行时值使用SimpleInjector同构造函数 [英] Pass runtime value to constructor using SimpleInjector

查看:282
本文介绍了传递运行时值使用SimpleInjector同构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想结合的 SimpleInjector与WebFormsMvp

要方便DI WebFormsMvp提供了 I presenterFactory 接口。
它包含创建方法,该方法提供了在 presenter型解决查看实例
我需要的注射查看实例放入的在 presenter在构造
在presenter 其他依赖这需要建立由容器

这是我有这么远,但它并不理想。
什么是对问题的正确的解决方案

presenter构造:

 公共富presenter(IFooView观点,IClientFactory clientFactory):基地(视图)
 

工厂:

 公共类SimpleInjector presenterFactory:我presenterFactory
{
    私人只读集装箱_container;
    私人IVIEW _currentView;

    公共SimpleInjector presenterFactory()
    {
        _container =新容器();

        FUNC<类型,BOOL> isIView =
            TYPE => typeof运算(IVIEW).IsAssignableFrom(类型);

        _container.ResolveUnregisteredType + =(S,E)=> {
            如果(isIView(e.UnregisteredServiceType))
                e.Register(()=> _currentView);
        };
    }

    公共我presenter创建(类型$ P ​​$ psenterType,类型viewType,IVIEW viewInstance)
    {
        锁定(_currentView)
        {
            _currentView = viewInstance;
            返回_container.GetInstance(presenterType),因为我presenter;
        }
    }
}
 

解决方案

WebFormsMvp迫使你采取的presenter构造函数中的观点,但是这会触发一个循环引用。如果你看一下在工厂实现了不同容器的,你会看到,他们做了不同的技巧来解决这个怪癖设计中的每个容器。例如,对于团结,他们创建一个子容器和注册的子容器这一观点,并使用该子容器解决presenter。很离奇和性能繁重。

而不是采取视图中的presenter构造

,WebFormsMvp的设计师应该让视野中的一个可写的属性我presenter 接口。这将使它尴尬容易地设置在presenter的看法。事情是这样的:

公开我presenter创建(类型$ P ​​$ psenterType,IVIEW视图) {     VAR presenter =(I presenter)_container.GetInstance(presenterType);     presenter.View =图。     返回presenter; }

不幸的是,他们并没有做到这一点,就不可能扩大的设计,让这个(没有做使用反射真的讨厌的东西)。

简单的喷油器不支持提供构造函数参数的的GetInstance()方法。有很好的理由,因为这通常会导致服务定位器反模式,你可以随时去解决这个通过改变设计。在你的情况,你没有作出这样古怪的设计,所以你不能改变它。

你做的 ResolveUnregisteredType 是pretty的聪明是什么。我也不会想到这个我自己。而且因为我背后的简单喷油器的领先开发,我在位置上说,你做了什么是真正聪明的: - )

就在两个点左右的反馈信息 SimpleInjector presenterFactory

首先,你应该提供容器的构造函数的参数,因为它会很容易,你需要到其他注册加入到容器中,而你不知道吨要注册容器 SimpleInjector presenterFactory

IVIEW>

其次,你可以使用 System.Threading.ThreadLocal&LT提高code 。这可以让你摆脱了全球锁。锁定prevents从正在同时任何presenter,而这可能会减慢你的网站。

所以这里有一个重构的版本:

公共类SimpleInjector presenterFactory:我presenterFactory {     私人只读集装箱_container;     私人ThreadLocal的< IVIEW> _currentView =新的ThreadLocal< IVIEW>();     公共SimpleInjector presenterFactory(集装箱货柜){         _container =容器;         _container.ResolveUnregisteredType + =(S,E)=> {             如果(typeof运算(IVIEW).IsAssignableFrom(e.UnregisteredServiceType)){                 e.Register(()=> _currentView.Value);             }         };     }     公共我presenter创建(类型$ P ​​$ psenterType,类型viewType,         IVIEW viewInstance)     {         _currentView.Value = viewInstance;         尝试 {             返回_container.GetInstance(presenterType),因为我presenter;         } 最后 {             //清除线程本地值,以保证             //视图可以在请求结束后进行处理。             _currentView.Value = NULL;         }     } }

如果你看一下团结presenterFactory 的实施,你看到了很多的缓存中就有事情。我不知道他们为什么这样做,但是从性能的角度来看,你根本不需要这样的事情,简单的注射器。也许我失去了一些东西,但我不明白为什么应该有一个缓存。

但更糟的是,有一个在团结presenterFactory 并发问题。看看这个方法:

 私有类型查找presenterDescribedViewTypeCached(类型$ P ​​$ psenter,
    IVIEW视图)
{
    IntPtr的处理= presenter.TypeHandle.Value;
    如果(!this.cache.ContainsKey(手柄))
    {
        锁定(this.syncLock)
        {
            如果(!this.cache.ContainsKey(手柄))
            {
                键入viewType = CreateType(presenter,视图);
                this.cache [处理] = viewType;
                返回viewType;
            }
        }
    }
    返回this.cache [处理]
}
 

乍一看,这code看起来不错,因为双重检查锁来实现。不幸的是,高速缓存(字典)从锁定外读取,而这是锁内更新。这不是线程安全的。相反,开发商应要么包裹在锁整个事情,使用 ConcurrentDictionary (.NET 4只),或考虑缓存不可变的,这意味着你创建原始字典的副本,添加新的价值,并更换参考旧字典,新的。然而,在这种情况下,我可能只是锁定了整个事情。

这是一个有点偏离主题,但只是想告诉: - )

I'm trying to combine SimpleInjector with WebFormsMvp.

To facilitate DI WebFormsMvp provides the IPresenterFactory interface.
It contains the Create method which provides the presenter type to resolve and the view instance.
I need to inject the view instance into the constructor of the presenter.
The presenter also has other dependencies that need creating by the container.

This is what I got so far, but it is not ideal.
What's the correct solution for the problem?

Presenter constructor:

public FooPresenter(IFooView view, IClientFactory clientFactory) : base(view)

Factory:

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 forces you to take the view in the constructor of the presenter, but this triggers a circular reference. If you look in the factory implementations for the different container's, you'll see that for each container they do a different trick to work around this quirk in the design. For instance, with unity, they create a child container and register that view in the child container, and resolve the presenter using that child container. Quite bizarre and performance heavy.

Instead of taking the view in the constructor of the presenter, the designers of WebFormsMvp should have made the View a writable property on the IPresenter interface. This would make it embarrassingly easy to set the view on the presenter. Something like this:

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

Unfortunately they didn't do this, and it is impossible to extend the design to allow this (without doing really nasty things using reflection).

Simple Injector does not support supplying constructor arguments to the GetInstance() method. For good reason, since this would typically lead to the Service Locator anti-pattern, and you can always go around this by changing the design. In your case, you didn't make that quirky design, so you can't change it.

What you did with the ResolveUnregisteredType is pretty clever. I wouldn't have thought about this myself. And since I'm the lead dev behind Simple Injector, I'm in the position to say that what you did was really clever :-)

Just two points of feedback about your SimpleInjectorPresenterFactory.

First of all, you should supply the Container as constructor argument, since it will be very likely that you need to add other registrations to the container, and you don't want to register the Container inside the SimpleInjectorPresenterFactory.

Secondly, you can improve the code by using a System.Threading.ThreadLocal<IView>. This allows you to get rid of the global lock. The lock prevents any presenter from being made concurrently, and this could slow down your website.

So here's a refactored version:

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;
        }
    }
}

If you look at the implementation of the UnityPresenterFactory, you see a lot of caching going on in there. I have no idea why they do that, but from a performance perspective, you don't need such thing for Simple Injector at all. Perhaps I'm missing something, but I don't see why there should be a cache.

But even worse, there is a concurrency bug in the UnityPresenterFactory. Take a look at this method:

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];
}

At first sight this code looks okay, since a double checked lock is implemented. Unfortunately, the cache (dictionary) is read from outside the lock, while it is updated inside the lock. This is not thread-safe. Instead, the developer should have either wrapped the whole thing in a lock, use a ConcurrentDictionary (.net 4 only) or consider the cache immutable, which means that you create a copy of the original dictionary, add the new value, and replace the reference to the old dictionary with the new. However, in this case I would probably just have locked the whole thing.

This was a little bit off topic, but just wanted to tell :-)

这篇关于传递运行时值使用SimpleInjector同构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆