使用与SignalR简单的喷油器(Using Simple Injector with Signal

2019-06-24 08:30发布

我想用我自己的IoC将是非常直截了当与SignalR,也许它是; 最有可能的,我做错了什么。 这里是我的代码,我到目前为止有:

private static void InitializeContainer(Container container)
{

   container.Register<IMongoHelper<UserDocument>, MongoHelper<UserDocument>>();
   // ... registrations like about and then:
   var resolver = new SimpleInjectorResolver(container);
   GlobalHost.DependencyResolver = resolver;
}

然后我的课:

public class SimpleInjectorResolver : DefaultDependencyResolver
{
    private Container _container;
    public SimpleInjectorResolver(Container container)
    {
        _container = container;
    }

    public override object GetService(Type serviceType)
    {
        return _container.GetInstance(serviceType) ?? base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.GetAllInstances(serviceType) ?? base.GetServices(serviceType);
    }
}

什么结束了发生的事情是,我得到一个错误,IJavaScriptProxyGenerator解决不了的,所以我觉得,嗯,我会添加注册:

container.Register<IJavaScriptProxyGenerator, DefaultJavaScriptProxyGenerator>(
    ConstructorSelector.MostParameters);

但随后有一堆别人的! 我得到:

container.Register<IDependencyResolver, SimpleInjectorResolver>();
container.Register<IJavaScriptMinifier, NullJavaScriptMinifier>();
container.Register<IJavaScriptProxyGenerator, DefaultJavaScriptProxyGenerator>(
    ConstructorSelector.MostParameters);
container.Register<IHubManager, DefaultHubManager>();
container.Register<IHubActivator, DefaultHubActivator>();
container.Register<IParameterResolver, DefaultParameterResolver>();
container.Register<IMessageBus, InProcessMessageBus>(ConstructorSelector.MostParameters);

这仍然给我“的类型没有注册ITraceManager可以找到。” ......但现在我想知道如果我在做这一切的权利,因为我不希望我需要重新布线一切SignalR是做......吧? 希望? 如果不是我会继续吃力地往前走,但我是一个SignalR和简单的注射器福利局所以想我会先询问。 :)

附加: https://cuttingedge.it/blogs/steven/pivot/entry.php?id=88因为SignalR有多个构造函数。

Answer 1:

嗯,我昨天试了,我已经找到了解决办法。 据我,在这里我想在SignalR依赖注入的唯一时刻是我中心:我不关心SignalR是如何工作的里面! 因此,而不是更换DependencyResolver,我创建了自己的实现IHubActivator的:

public class SimpleInjectorHubActivator : IHubActivator
{
    private readonly Container _container;

    public SimpleInjectorHubActivator(Container container)
    {
        _container = container;
    }

    public IHub Create(HubDescriptor descriptor)
    {
        return (IHub)_container.GetInstance(descriptor.HubType);
    }
}

那我可以像这样(在的Application_Start)注册:

var activator = new SimpleInjectorHubActivator(container);
GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => activator);
RouteTable.Routes.MapHubs();


Answer 2:

想扔我的2美分在这里与其他的答案,这可能是在寻找与SignalR依赖注入你自己的方式帮助,或者使用SimpleInjector或其它IoC。

使用@史蒂芬的答案

如果你决定使用史蒂芬的回答,请确保您撰写的根,然后注册您的枢纽航线。 该SignalRRouteExtensions.MapHubs扩展方法(又名routes.MapHubs()将调用Register(Type, Func<object>)GlobalHost.DependencyResolver映射所述毂路由时,因此,如果换出DefaultDependencyResolver与史蒂芬的SimpleInjectorResolver的航线之前映射,你会碰上他的NotSupportedException

使用@Nathanael马尔尚的回答

这是我最喜欢的。 为什么?

  1. 更少的代码比SimpleInjectorDependencyResolver
  2. 无需更换DefaultDependencyResolver (又名GlobalHost.DependencyResolver ),这意味着更少的代码。
  3. 您可以映射枢纽航线之前或之后撰写的根源,因为你没有更换DefaultDependencyResolver ,它会“只是工作”。

就像拿但业虽然说,如果你关心你的依赖,这是唯一的Hub类,这将可能是大多数的情况。 如果你想更动注入其他依赖到SignalR,你可能想要去与史蒂芬的答案。

在与每个Web请求的依赖问题Hub

有一个关于SignalR一件有趣的事情......当一个客户端(通过关闭浏览器窗口为例)从集线器断开,它会创建一个新的实例Hub ,以类调用OnDisconnected() 发生这种情况时, HttpContext.Current为null。 因此,如果这个Hub具有的每个Web请求注册的任何依赖关系, 一些可能会出问题

在Ninject

我尝试了使用Ninject SignalR依赖注入和上的NuGet ninject signalr依赖解析器 。 根据该结构,被绑定依赖性.InRequestScope()将被瞬时当注入创建Hub断开事件期间。 由于HttpContext.Current是空的,我想Ninject只是决定忽略它并没有告诉你创建的瞬态实例。 也许有一个配置设置来告诉ninject警告这一点,但它是不是默认。

在SimpleInjector

当SimpleInjector,另一方面将抛出一个异常Hub取决于与注册的实例WebRequestLifestlyle

注册委托类型NameOfYourHub抛出异常。 注册委托类型NameOfYourPerRequestDependency抛出异常。 该YourProject.Namespace.NameOfYourPerRequestDependency注册为“PerWebRequest”,但实例被请求的HttpContext上下文之外(HttpContext.Current为null)。 使用这种生活方式确保实例在应用程序初始化阶段不解决,并在后台线程中运行时。 为了解决在后台线程的情况下,尝试注册该实例为“每终生范围”: https://simpleinjector.readthedocs.io/en/latest/lifetimes.html#scoped 。

...注意这个异常只会泡了当HttpContext.Current == null ,其中据我所知,只有发生在SignalR请求Hub ,以实例来调用OnDisconnected()

在对每个Web请求的依赖性解决方案Hub

请注意,这些都不是真正理想的,这完全取决于你的应用需求。

在Ninject

如果您需要非暂时性的依赖关系,只是不重写OnDisconnected()或与类的依赖关系做任何定制那里。 如果你这样做,图中的每一个依赖将是一个独立的(瞬态)的实例。

在SimpleInjector

你需要一个混合的生活方式之间WebRequestLifestlye ,要么Lifestyle.TransientLifestyle.Singleton ,或LifetimeScopeLifestyle 。 当HttpContext.Current不为空,依赖性只会活,只要web请求,你通常会期望。 然而,当HttpContext.Current为空时,依赖将任一瞬时注入,如单身,或寿命范围之内。

var lifestyle = Lifestyle.CreateHybrid(
    lifestyleSelector: () => HttpContext.Current != null,
    trueLifestyle: new WebRequestLifestyle(),
    falseLifestyle: Lifestyle.Transient // this is what ninject does
    //falseLifestyle: Lifestyle.Singleton
    //falseLifestyle: new LifetimeScopeLifestyle()
);

更多关于LifetimeScopeLifestyle

就我而言,我有一个的EntityFramework DbContext依赖。 这些可能会非常棘手,因为当短暂注册或单身人士,他们可以揭露问题。 当瞬时注册,您可以例外结束尝试与连接到2个或多个实体合作DbContext实例。 当作为一个单独注册,你最终得到更普遍的异常(永远不要注册DbContext作为一个单身)。 在我来说,我需要DbContext到一个特定的生命周期中,同一个实例可以在多个嵌套操作被重复使用,这意味着我需要的内生活LifetimeScopeLifestyle

现在,如果你使用上面的混合代码falseLifestyle: new LifetimeScopeLifestyle()行,你会得到另一个异常,当您的自定义IHubActivator.Create方法执行:

注册委托类型NameOfYourHub抛出异常。 所述NameOfYourLifetimeScopeDependency被登记为“LifetimeScope”,但该实例被请求的寿命范围的上下文之外。 请务必先调用container.BeginLifetimeScope()。

你已经建立了一个终身范围依赖的方式是这样的:

using (simpleInjectorContainer.BeginLifetimeScope())
{
    // resolve solve dependencies here
}

这与寿命范围内注册的任何依赖关系必须在此范围内解决using块。 此外,如果任何这些依赖的实施IDisposable ,它们将被在的端部设置的using块。 不要试图做这样的事情:

public IHub Create(HubDescriptor descriptor)
{
    if (HttpContext.Current == null)
        _container.BeginLifetimeScope();
    return _container.GetInstance(descriptor.HubType) as IHub;
}

我问史蒂芬(谁也恰好是万一SimpleInjector作者你不知道)这事,他说:

嗯..如果不处置LifetimeScope,你就麻烦大了,所以一定要确保他们得到处置。 如果不处理的范围,他们将流连曾经在ASP.NET。 这是因为范围可以被嵌套和引用它们的父范围。 因此,一个线程保持活着的最内部范围(其缓存)范围内保持活着其父范围(其缓存)等。 ASP.NET池线程,不重置所有值时,它抓住从池中的线程,所以这意味着所有范围活路,下一次你从池中获取一个线程,开始新的生命周期范围内,你可以简单的创建一个新的嵌套范围,这将让堆放。 迟早,你会得到一个OutOfMemoryException。

不能使用IHubActivator到范围的依赖,因为它不活,只要该Hub它创建实例。 所以,即使你包裹BeginLifetimeScope()方法中的using块,你的依赖性会后立即置于Hub创建实例。 你真正需要的是在这里的间接另一层。

我结束了,其中大部分归功于史蒂芬的帮助,是一个命令装饰(和查询装饰)。 一个Hub不能依赖于每个Web请求实例本身,而必须依赖它的实现依赖于每个请求的情况下,另外的接口上。 被注入的实现Hub构造函数(通过simpleinjector)与开始和寿命范围部署的包装装潢。

public class CommandLifetimeScopeDecorator<TCommand> : ICommandHandler<TCommand>
{
    private readonly Func<ICommandHandler<TCommand>> _handlerFactory;
    private readonly Container _container;

    public CommandLifetimeScopeDecorator(
        Func<ICommandHandler<TCommand>> handlerFactory, Container container)
    {
        _handlerFactory = handlerFactory;
        _container = container;
    }

    [DebuggerStepThrough]
    public void Handle(TCommand command)
    {
        using (_container.BeginLifetimeScope())
        {
            var handler = _handlerFactory(); // resolve scoped dependencies
            handler.Handle(command);
        }
    }
}

...它是装饰ICommandHandler<T>依赖于每个网络的请求的实例的实例。 对于所使用的模式的更多信息,请阅读这个和这个 。

示例注册

container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>), assemblies);

container.RegisterSingleDecorator(
    typeof(ICommandHandler<>),
    typeof(CommandLifetimeScopeDecorator<>)
);


Answer 3:

更新这个答案已经更新了SignalR 1.0版本

这是如何建立一个SignalR IDependencyResolver为简单喷油器:

public sealed class SimpleInjectorResolver 
    : Microsoft.AspNet.SignalR.IDependencyResolver
{
    private Container container;
    private IServiceProvider provider;
    private DefaultDependencyResolver defaultResolver;

    public SimpleInjectorResolver(Container container)
    {
        this.container = container;
        this.provider = container;
        this.defaultResolver = new DefaultDependencyResolver();
    }

    [DebuggerStepThrough]
    public object GetService(Type serviceType)
    {
        // Force the creation of hub implementation to go
        // through Simple Injector without failing silently.
        if (!serviceType.IsAbstract && typeof(IHub).IsAssignableFrom(serviceType))
        {
            return this.container.GetInstance(serviceType);
        }

        return this.provider.GetService(serviceType) ?? 
            this.defaultResolver.GetService(serviceType);
    }

    [DebuggerStepThrough]
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return this.container.GetAllInstances(serviceType);
    }

    public void Register(Type serviceType, IEnumerable<Func<object>> activators)
    {
        throw new NotSupportedException();
    }

    public void Register(Type serviceType, Func<object> activator)
    {
        throw new NotSupportedException();
    }

    public void Dispose()
    {
        this.defaultResolver.Dispose();
    }
}

不幸的是,与设计问题DefaultDependencyResolver 。 这就是为什么上面的实施并没有继承它,而是把它包装。 我创建的SignalR网站这个问题。 你可以阅读一下这里 。 虽然设计者同意我的意见,遗憾的是这个问题还没有被固定在1.0版本。

我希望这有帮助。



Answer 4:

从SignalR 2.0(或公测的)有设置依赖解析器的新方法。 SignalR搬到OWIN启动做配置。 有了简单喷油你会做这样的:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = new HubConfiguration()
        {
            Resolver = new SignalRSimpleInjectorDependencyResolver(Container)
        };
        app.MapSignalR(config);
    }
}

public class SignalRSimpleInjectorDependencyResolver : DefaultDependencyResolver
{
    private readonly Container _container;
    public SignalRSimpleInjectorDependencyResolver(Container container)
    {
        _container = container;
    }
    public override object GetService(Type serviceType)
    {
        return ((IServiceProvider)_container).GetService(serviceType)
               ?? base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.GetAllInstances(serviceType)
            .Concat(base.GetServices(serviceType));
    }
}

你必须明确地注入你的集线器,如下所示:

container.Register<MessageHub>(() => new MessageHub(new EFUnitOfWork()));

这个配置是在一个高流量的网站实时运行没有问题。



Answer 5:

以下为我工作。 此外,您将需要实例化依赖解析器之前注册与集线器类容器的委托。

ex: container.Register<MyHub>(() =>
        {
            IMyInterface dependency = container.GetInstance<IMyInterface>();

            return new MyHub(dependency);
        });

public class SignalRDependencyResolver : DefaultDependencyResolver
{
    private Container _container;
    private HashSet<Type> _types = new HashSet<Type>();

    public SignalRDependencyResolver(Container container)
    {
        _container = container;

        RegisterContainerTypes(_container);
    }

    private void RegisterContainerTypes(Container container)
    {
        InstanceProducer[] producers = container.GetCurrentRegistrations();

        foreach (InstanceProducer producer in producers)
        {
            if (producer.ServiceType.IsAbstract || producer.ServiceType.IsInterface)
                continue;

            if (!_types.Contains(producer.ServiceType))
            {
                _types.Add(producer.ServiceType);
            }
        }
    }

    public override object GetService(Type serviceType)
    {
        return _types.Contains(serviceType) ? _container.GetInstance(serviceType) : base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _types.Contains(serviceType) ? _container.GetAllInstances(serviceType) : base.GetServices(serviceType);
    }
}


文章来源: Using Simple Injector with SignalR