我想知道如何注册类并设置Simple Injector容器以按以下方式实例化类。即从手动DI转到具有以下Consumer类,注入CompositeService,对象图和生命周期设置如下:
为了引入一些上下文(如果它有帮助),Consumer类可能是来自WPF应用程序的ViewModel,它在请求View时被实例化。
public class Consumer
{
public Consumer()
{
var sharedSvc = new SharedService();
var productSvc = new ProductService(sharedSvc, new MathHelper());
var compositeSvc = new CompositeService(sharedSvc, productSvc, new MathHelper());
compositeSvc.Process();
}
}
其中: MyContext应该在调用范围内共享。 ProductService和CompositeService可以是瞬态的,也可以在范围内共享。 MathHelper必须是瞬态的。
问:如何使用Simple Injector实现上述目标?
或更具体地说: 如何在Simple Injector Scope的上下文中实例化多个MathHelper?
我已阅读http://simpleinjector.readthedocs.org/en/latest/lifetimes.html 并阅读并遵循SO回答https://stackoverflow.com/a/29808487/625113但是, 似乎一切都可以是瞬态的或范围的,但不是某些特定的对象范围和其余的瞬态(这似乎很奇怪)。
以下使用Simple Injector将实现SharedService结果,但是如果我希望ProductService和CompositeService也具有作用域生存期,那么它将不起作用:
cont.RegisterLifetimeScope<SharedService>();
cont.Register<MathHelper>();
cont.Register<ProductService>();
cont.Register<CompositeService>();
using (cont.BeginLifetimeScope())
{
var compositeSvc = cont.GetInstance<CompositeService>();
compositeSvc.Process();
}
如果我将ProductService或CompositeService中的一个或两个注册为RegisterLifetimeScope,则会出现Lifetime不匹配异常。即
cont.RegisterLifetimeScope<SharedService>();
cont.Register<MathHelper>();
cont.RegisterLifetimeScope<ProductService>();
cont.Register<CompositeService>(); // or cont.RegisterLifetimeScope<CompositeService>();
using (cont.BeginLifetimeScope())
{
var compositeSvc = cont.GetInstance<CompositeService>(); // Exception thrown
compositeSvc.Process();
}
引发此页面的异常:https://simpleinjector.readthedocs.org/en/latest/LifestyleMismatches.html
关于Singleton,我可以依赖于Transient,并且可以推断出一种理解,即在这种情况下可以说Simple Injector警告Scoped不能依赖Transient,因为Transient isn& #39;在范围内进行管理。
所以我的问题更具体地说明如何在Simple Injector Scope的上下文中实例化多个MathHelper?
简要背景 - 出现这种情况,因为我有一个4年历史,2层,基于WPF的应用程序,目前正在使用Ninject,它具有@Steven描述的臃肿的混合服务架构 在他的博客系列中(即服务已成为混合,半相关,命令和查询的混搭)。大多数这些服务都是分离出去的好选择 ICommandHandler / IQueryHandler架构......但是你不可能在一夜之间做事,所以首先要解决从Ninject到SimpleInjector的问题(是的,我知道Ninject可以对这个架构做同样的事情) 但是还有其他原因可以转移到SimpleInjector)。
至于&#34;范围&#34;依赖性解决方案,&#34;范围&#34; (在这个应用程序中)被认为是一个表单的生命,所以一个DbContext(如上例中的SharedService)是共享的 表单/ viewModel需要的服务和服务的MOST是每个作用域,一些注入的服务或辅助类需要作为Transient注入。
这(对我而言)类似于Mark Seemann的http://blog.ploeh.dk/2014/06/03/compile-time-lifetime-matching/手动编码示例,其中有一个Per Request(单一作用域)服务,
注入瞬态物体。
编辑:我误读了Mark Seemann的例子并且正在阅读代码,好像 BarController 是一项服务。因此,虽然BarController对象组成是相同的,但生命周期却没有。这就是说 SomeThreadUnsafeService 可以很容易地注入一个新的SomeServiceThatMustBeTransient但是,我纠正了,他的例子并没有这样做。
因此我想知道如何在简单注入器中完成Mark Seemann所做的对象组合,但是在web reqeusts的背景之外(我的假设是Simple Injector&#39; s 每个Web请求范围本质上是特定类型的Lifetime范围。)
为了解决@Steve和@Ric .net的评论和回答,我可以看到有可能最终出现2个不同服务使用另一个使用瞬态对象的共享服务的场景(存储状态)和所谓的瞬态对象成为一个 Singleton Scoped对象在&#34; some&#34;那些服务。例如
public class SingletonScopedService1
{
private readonly TransientX _transientA;
public SingletonScopedService1(TransientX transientA)
{
_transientA = transientA;
}
public void PokeTransient()
{
_transientA.Poke();
}
}
public class SingletonScopedService2
{
private readonly SingletonScopedService1 _service1;
private readonly TransientX _transientB;
public SingletonScopedService2(SingletonScopedService1 service1, TransientX transientB)
{
_service1 = service1;
_transientB = transientB;
}
public void GoFishing()
{
_service1.PokeTransient();
// This TransientX instance isn't affected
_transientB.Poke();
}
}
public class SingletonService3
{
private readonly SingletonScopedService1 _service1;
public SingletonService3(SingletonScopedService1 service1)
{
_service1 = service1;
}
public void DoSomething()
{
_service1.PokeTransient();
}
}
如果在SingletonScopedService3上调用DoSomething()并在SingletonScopedService2上调用GoFishing()(假设TransientX维持状态),则结果&#34;可能&#34;根据TransientX的目的而出乎意料。
所以我很高兴接受这个,因为申请按预期运作(但也接受目前的成分很脆弱)。
话虽如此,我的原创作品或Mark Seemann的例子可以在Simple Injector中注册所需的生命周期,还是严格来说不可能通过设计,更好地手动编写对象 对于需要这样做的实例,可以使用图形(或者为@Ric .net建议注入一个Func),直到可以进行进一步的重构/加固为止?
虽然Ninject允许您注册我的原始作品,如:
var kernel = new StandardKernel();
kernel.Bind<SharedService>().ToSelf().InCallScope();
kernel.Bind<MathHelper>().ToSelf();
kernel.Bind<ProductService>().ToSelf().InCallScope();
kernel.Bind<CompositeService>().ToSelf().InCallScope();
设计的简单注射器没有,我相信我的第二个例子就是为什么。
仅供参考:在我的实际案例中,对于对象图有这个的少数几个实例,我手动构建了图形(只是为了切换到Simple Injector),意图重构这些潜在的问题
答案 0 :(得分:3)
正如链接的SO question中所解释的那样Lifestyle Mismatch exception
基本上说的是,当你让一个对象依赖于另一个具有另一个对象时,你正在创建一个所谓的captive dependency更短的生活方式。
虽然这对你来说可能听起来很奇怪,但是俘虏依赖是:
如果MathHelper
具有可变状态,因此需要在其他服务中注入瞬态,MathHelper
已成为某种运行时数据&#39;。并injecting runtime data is an anti-pattern。
此博客文章详细描述了运行时数据的问题以及如何解决此问题。如上所述解决您的问题还有很长的路要走!
由于您没有指定MathHelper
的实施细节,因此很难向您提供有关如何重构MathHelper
的建议。因此,我能给你的唯一建议是,让运行时数据或者说“#”;作为消息流过系统。您可以阅读有关基于邮件的设计here和here。
然而,还有其他几个选项可行,但IMO并不是很好的设计。所以建议不要使用这些,而是要完整:
不是将MathHelper
作为瞬态注入,而是注入MathHelperProvider
甚至更简单的注入Func<MathHelper>
,您可以注册为单身:
container.RegisterSingleton<Func<MathHelper>>(() => container.GetInstance<MathHelper>());
请注意,通过注册委托,您将使容器失明。它不再能够警告您对象图的这一部分中的错误配置。
我想到的其他解决方案在设计上是如此丑陋,在写完之后,我决定将它们排除在这个答案之外!
如果你要添加一些关于为什么MathHelper
需要短暂的细节,我可以给你一些建议,你可以做一些调整,以使其成为范围甚至更好:单身。