我认为使用我自己的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和简单的注射器newb所以我想我先问。 :)
附加:https://cuttingedge.it/blogs/steven/pivot/entry.php?id=88,因为SignalR有多个构造函数。
答案 0 :(得分:43)
好吧,我昨天试过,我找到了一个解决方案。 据我所知,我想在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();
答案 1 :(得分:24)
想要将其他答案扔到这里2美分,这有助于在SignalR中使用SimpleInjector或其他IoC找到自己的依赖注入方式。
如果您决定使用Steven的答案,请确保在撰写根之前注册中心路由。映射中心路由时,SignalRRouteExtensions.MapHubs
扩展方法(又名routes.MapHubs()
)会在Register(Type, Func<object>)
上调用GlobalHost.DependencyResolver
,因此如果您将DefaultDependencyResolver
换成Steven&# 39;在路由映射之前的SimpleInjectorResolver
,您将遇到他的NotSupportedException
。
这是我的最爱。为什么呢?
SimpleInjectorDependencyResolver
。DefaultDependencyResolver
(a.k.a。GlobalHost.DependencyResolver
),这意味着更少的代码。DefaultDependencyResolver
,它将&#34;只是工作&#34;。 就像Nathanael所说的那样,只有当你关心Hub
类的依赖关系时才会这样,大多数人都可能会这样。如果你想把其他依赖注入SignalR,你可能想要接受史蒂文的回答。
Hub
有关SignalR的一个有趣的事情......当客户端与集线器断开连接时(例如关闭浏览器窗口),它将创建Hub
类的新实例以便调用{{ 1}}。发生这种情况时, OnDisconnected()
为空。因此,如果此HttpContext.Current
具有按网络请求注册的任何依赖项,那么可能会出错。
我尝试使用Ninject和ninject signalr dependency resolver on nuget进行SignalR依赖注入。使用此配置,在断开连接事件期间注入Hub
时,将临时创建绑定.InRequestScope()
的依赖项。由于Hub
为空,我想Ninject只是决定忽略它并在不告诉你的情况下创建瞬态实例。也许有一个配置设置告诉ninject警告这个,但它不是默认值。
HttpContext.Current
依赖于在Hub
注册的实例时,SimpleInjector会抛出异常:
NameOfYourHub类型的已注册委托引发了异常。该 类型NameOfYourPerRequestDependency的已注册委托引发了异常。该 YourProject.Namespace.NameOfYourPerRequestDependency注册为 &#39; PerWebRequest&#39;,但是在上下文之外请求实例 一个HttpContext(HttpContext.Current为null)。确保实例使用 应用程序初始化期间未解决此生活方式 阶段和在后台线程上运行时。用于解析实例 在后台线程上,尝试将此实例注册为&#39; Per Lifetime 范围&#39;:https://simpleinjector.readthedocs.io/en/latest/lifetimes.html#scoped。
...请注意,只有WebRequestLifestlyle
HttpContext.Current == null
时才会出现此异常,只有在我知道时,才会发生SignalR请求Hub
实例以调用OnDisconnected()
的情况。
Hub
请注意,这些都不是理想的,它们都取决于您的应用要求。
如果您需要非瞬态依赖项,请不要覆盖OnDisconnected()
或使用类依赖项执行任何自定义操作。如果这样做,图中的每个依赖项将是一个单独的(瞬态)实例。
WebRequestLifestlye
与Lifestyle.Transient
,Lifestyle.Singleton
或LifetimeScopeLifestyle
之间需要hybrid lifestyle。当HttpContext.Current
不为空时,依赖关系只会像您通常期望的那样生存。但是,当HttpContext.Current
为null时,依赖关系将作为单例或单生命范围内的瞬时注入。
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注册为&#39; LifetimeScope&#39;, 但是在生命范围的上下文之外请求实例。 确保首先调用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
实例后立即处理。你真正需要的是另一层间接。
我非常感谢Steven的帮助,是一个命令装饰器(以及一个查询装饰器)。 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);
}
}
}
...依赖于每个Web请求实例的装饰ICommandHandler<T>
实例。有关所用模式的详细信息,请阅读this和this。
container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>), assemblies);
container.RegisterSingleDecorator(
typeof(ICommandHandler<>),
typeof(CommandLifetimeScopeDecorator<>)
);
答案 2 :(得分:5)
更新 此答案已针对SignalR版本1.0进行了更新
这是为Simple Injector构建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网站上创建了一个关于此的问题。你可以阅读它here。虽然设计师同意我的意见,但不幸的是1.0版本中没有修复该问题。
我希望这会有所帮助。
答案 3 :(得分:4)
从SignalR 2.0(以及beta版)开始,有一种设置依赖关系解析器的新方法。 SignalR转移到OWIN启动以进行配置。使用Simple Injector,您可以这样做:
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()));
此配置在高流量网站上正常运行,没有任何问题。
答案 4 :(得分:0)
以下对我有用。此外,在实例化依赖项解析程序之前,您需要在集线器类的容器中注册委托。
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);
}
}