我想用我自己的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有多个构造函数。
嗯,我昨天试了,我已经找到了解决办法。 据我,在这里我想在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();
想扔我的2美分在这里与其他的答案,这可能是在寻找与SignalR依赖注入你自己的方式帮助,或者使用SimpleInjector或其它IoC。
使用@史蒂芬的答案
如果你决定使用史蒂芬的回答,请确保您撰写的根,然后注册您的枢纽航线。 该SignalRRouteExtensions.MapHubs
扩展方法(又名routes.MapHubs()
将调用Register(Type, Func<object>)
上GlobalHost.DependencyResolver
映射所述毂路由时,因此,如果换出DefaultDependencyResolver
与史蒂芬的SimpleInjectorResolver
的航线之前映射,你会碰上他的NotSupportedException
。
使用@Nathanael马尔尚的回答
这是我最喜欢的。 为什么?
- 更少的代码比
SimpleInjectorDependencyResolver
。 - 无需更换
DefaultDependencyResolver
(又名GlobalHost.DependencyResolver
),这意味着更少的代码。 - 您可以映射枢纽航线之前或之后撰写的根源,因为你没有更换
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.Transient
, Lifestyle.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<>)
);
更新这个答案已经更新了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版本。
我希望这有帮助。
从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()));
这个配置是在一个高流量的网站实时运行没有问题。
以下为我工作。 此外,您将需要实例化依赖解析器之前注册与集线器类容器的委托。
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);
}
}