gpt4 book ai didi

c# - 如何使用Simple Injector实现此混合对象生命周期

转载 作者:行者123 更新时间:2023-11-30 21:50:51 27 4
gpt4 key购买 nike

我想知道如何注册这些类并设置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必须是瞬态的。

问:如何使用简单注射器实现以上目标?

或更具体地说:
如何在简单注入器作用域的范围内实例化多个MathHelper?

我已经阅读过 http://simpleinjector.readthedocs.org/en/latest/lifetimes.html
并阅读并遵循SO答案 https://stackoverflow.com/a/29808487/625113,但是,
似乎所有东西都可以是瞬态的或作用域的,但某些特定对象不是作用域的,其余的都是瞬态的(这似乎很奇怪)。

更新1

以下与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不在内部管理范围。

所以我的问题是更具体地讲,如何在“简单注入器作用域”的上下文中实例化多个MathHelper?

更新2-进一步的背景和示例

简要背景-这种情况出现在我有一个使用Ninject的4岁,2层,基于WPF的应用程序时,该应用程序具有@Steven描述的@肿的混合服务体系结构
在他的博客系列中(即服务已变成混合,半相关,命令和查询的混合物)。这些服务中的大多数都是分离为
ICommandHandler / IQueryHandler体系结构...但是您不能一overnight而就,因此首先要进行的工作是将Ninject转换为SimpleInjector(是的,我知道Ninject可以在此体系结构上做同样的事情
但还有其他原因需要移至SimpleInjector)。

就“确定范围”依赖项解析而言,“范围”(在此应用程序中)被视为在表单的整个生命周期内,因此,一个DbContext(例如上面示例中的SharedService)被共享。
form / 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如何在Simple Injector中进行对象合成,但是在Web需求范围之外(我的假设是Simple Injector的
本质上,每个Web请求范围是生命周期范围的一种特定类型。

为了解决@Steve和@Ric .net的评论和答案,我可以看到有可能出现以下情况:两个不同的服务使用另一个共享服务,该共享服务使用一个临时对象(存储状态)和所谓的临时对象成为
这些服务的“某些”上下文中的Singleton Scoped对象。例如

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保持状态),则根据TransientX的目的,结果“可能”是意外的。

因此,我很高兴接受这一点,因为该应用程序可以按预期运行(但也接受当前的组合是脆弱的)。

话虽如此,我的原始构图或Mark Seemann的示例是否可以在Simple Injector上以所需的使用寿命进行注册,或者是通过设计严格地做到这一点是不可能的,而是更好地手动组成对象
图(或按@Ric .net建议插入Func)在需要进一步重构/强化之前需要这样做的实例?

更新3-结论

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并非如此,我相信我的第二个例子就是一个为什么的例子。

仅供参考:在我的实际案例中,对于对象图具有这种情况的少数情况,我手动构造了图(只是为了切换到Simple Injector),目的是将这些潜在问题重构出去。

最佳答案

如链接的SO question中所述,Lifestyle Mismatch exception的基本含义是,当您让一个对象依赖具有较短生活方式的另一个对象时,您正在创建一个所谓的captive dependency

虽然这听起来可能很奇怪,但专属的依存关系是:


一个现实生活中的问题,如果未被发现,通常会导致非常奇怪的错误,很难调试
如史蒂文(Steven)所指出的,该服务具有某种状态,这永远不是一个好兆头


如果MathHelper具有可变状态,因此需要作为瞬态注入到其他服务中,则MathHelper已成为某种“运行时数据”。还有injecting runtime data is an anti-pattern

该博客文章详细描述了运行时数据存在的问题以及如何解决此问题。按照描述的方法解决问题,还有路要走!

因为您没有指定MathHelper的实现细节,所以很难给您一些建议,您应该如何重构MathHelper。因此,我唯一能给您的建议就是让运行时数据或“状态”作为消息流经系统。您可以阅读有关基于消息的设计herehere的信息。

但是,还有其他一些选项可以使用,但不是IMO的良好设计。因此建议不要使用这些,而是​​要完整:

代替注入MathHelper作为瞬态,可以注入MathHelperProvider或更简单的注入Func<MathHelper>,您可以将其注册为单例:

container.RegisterSingleton<Func<MathHelper>>(() => container.GetInstance<MathHelper>());


请注意,通过注册代表,您将使容器变得盲目。在对象图的这一部分,它将无法再警告您配置错误。

我想到的其他解决方案在设计上是如此丑陋,以至于在编写它们之后,我决定将其排除在答案之外!

如果您要添加有关 MathHelper为什么需要瞬态的一些详细信息,我可以为您提供一些建议,您可以在其中进行调整以使范围变大甚至更好:单例。

关于c# - 如何使用Simple Injector实现此混合对象生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36076451/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com