gpt4 book ai didi

c# - 简单注入(inject)器:跨相同图的服务注入(inject)相同的UnitOfWork实例

转载 作者:太空狗 更新时间:2023-10-29 18:08:36 24 4
gpt4 key购买 nike

我有多个服务,每个服务都使用Simple Injectorioc容器将UnitOfWork注入构造函数。
目前,我可以看到每个UnitOfWork实例都是一个单独的对象,这很糟糕,因为我使用的是实体框架,并且在所有工作单元中都需要相同的上下文引用。
如何确保每个解析请求都向所有服务注入相同的UnitOfWork实例?命令完成后,外部命令处理程序decorator将保存myUnitOfWor
请注意,这是一个公共库,将用于MVC和Windows窗体,如果可能的话,最好为这两个平台都提供通用解决方案。
代码如下:

// snippet of code that registers types
void RegisterTypes()
{
// register general unit of work class for use by majority of service layers
container.Register<IUnitOfWork, UnitOfWork>();

// provide a factory for singleton classes to create their own units of work
// at will
container.RegisterSingle<IUnitOfWorkFactory, UnitOfWorkFactory>();

// register logger
container.RegisterSingle<ILogger, NLogForUnitOfWork>();

// register all generic command handlers
container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());

container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>));

// register services that will be used by command handlers
container.Register<ISynchronisationService, SynchronisationService>();
container.Register<IPluginManagerService, PluginManagerService>();
}

下一行的预期结果是创建一个对象,该对象在整个构造的对象图中具有一个共享的UnitOfWork实例:
var handler = Resolve<ICommandHandler<SyncExternalDataCommand>>();

以下是我的服务:
public class PluginManagerService : IPluginSettingsService
{
public PluginManagerService(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}

private readonly unitOfWork;

void IPluginSettingsService.RegisterPlugins()
{
// manipulate the unit of work
}
}

public class SynchronisationService : ISynchronisationService
{
public PluginManagerService(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}

private readonly unitOfWork;

void ISynchronisationService.SyncData()
{
// manipulate the unit of work
}
}

public class SyncExternalDataCommandHandler
: ICommandHandler<SyncExternalDataCommand>
{
ILogger logger;
ISynchronisationService synchronisationService;
IPluginManagerService pluginManagerService;

public SyncExternalDataCommandHandler(
ISynchronisationService synchronisationService,
IPluginManagerService pluginManagerService,
ILogger logger)
{
this.synchronisationService = synchronisationService;
this.pluginManagerService = pluginManagerService;
this.logger = logger;
}

public void Handle(SyncExternalDataCommand command)
{
// here i will call both services functions, however as of now each
// has a different UnitOfWork reference internally, we need them to
// be common.
this.synchronisationService.SyncData();
this.pluginManagerService.RegisterPlugins();
}
}

最佳答案

你需要哪种注册取决于申请的类型。因为您正在讨论两个不同的框架(mvc和winforms),所以这两个框架都有不同的注册。
对于mvc应用程序(或一般的web应用程序),最常见的做法是在per web request basis上注册工作单元。例如,以下注册将在单个web请求期间缓存工作单元:

container.Register<IUnitOfWork>(() =>
{
var items = HttpContext.Current.Items;

var uow = (IUnitOfWork)items["UnitOfWork"];

if (uow == null)
{
items["UnitOfWork"] = uow = container.GetInstance<UnitOfWork>();
}

return uow;
});

这种注册的缺点是工作单元没有被处理(如果需要)。对于向容器添加扩展方法的简单注入器,存在 an extension package,它将自动确保在web请求结束时释放实例。使用此软件包,您将能够进行以下注册:
container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();

这是通往:
container.Register<IUnitOfWork, UnitOfWork>(new WebRequestLifestyle());

另一方面,windows窗体应用程序通常是单线程的(单个用户将使用该应用程序)。我认为每个表单有一个单独的工作单元并不罕见,它在表单关闭时被释放,但是随着命令/处理程序模式的使用,我认为最好采用更面向服务的方法。我的意思是,最好的设计方式是,您可以将业务层移动到wcf服务,而无需更改表示层。您可以通过让命令只包含原语和(其他) DTOs来实现这一点。因此不要将实体框架实体存储到命令中,因为这会使命令的序列化变得更加困难,并且会在以后导致意外。
当您这样做时,在命令处理程序开始执行之前创建一个新的工作单元,在该处理程序执行期间重用同一个工作单元,并在处理程序成功完成时提交它(并始终对其进行处理)将非常方便。这是 Per Lifetime Scope lifestyle的典型情况。有 an extension packageRegisterPerWebRequest扩展方法添加到容器中。使用此软件包,您将能够进行以下注册:
container.RegisterLifetimeScope<IUnitOfWork, UnitOfWork>();

这是通往:
container.Register<IUnitOfWork, UnitOfWork>(new LifetimeScopeLifestyle());

然而,注册只是故事的一半。第二部分是决定何时保存工作单位的变化,以及在使用终身生活方式的情况下,从何处开始和结束这样一个范围。因为您应该在命令执行之前显式地启动一个生存期作用域,并在命令完成执行时结束它,所以最好的方法是使用命令处理程序decorator,它可以包装您的命令处理程序。因此,对于表单应用程序,您通常会注册一个额外的命令处理程序decorator来管理生存期范围。这种方法在这种情况下不起作用。请看一下下面的装饰,但请注意它是不正确的:
private class LifetimeScopeCommandHandlerDecorator<T>
: ICommandHandler<T>
{
private readonly Container container;
private readonly ICommandHandler<T> decoratedHandler;

public LifetimeScopeCommandHandlerDecorator(...) { ... }

public void Handle(T command)
{
using (this.container.BeginLifetimeScope())
{
// WRONG!!!
this.decoratedHandler.Handle(command);
}
}
}

此方法不起作用,因为修饰的命令处理程序是在生命周期范围启动之前创建的。
我们可能会尝试如下解决此问题,但这也不正确:
using (this.container.BeginLifetimeScope())
{
// EVEN MORE WRONG!!!
var handler = this.container.GetInstance<ICommandHandler<T>>();

handler.Handle(command);
}

尽管在生存期作用域的上下文中请求 RegisterLifetimeScope,确实会为该作用域注入 ICommandHandler<T>,但容器将返回一个(再次)用 IUnitOfWork修饰的处理程序。因此,调用 LifetimeScopeCommandHandlerDecorator<T>将导致递归调用,最后将出现堆栈溢出异常。
问题是依赖关系图在我们启动生存期作用域之前已经构建好了。因此,我们必须通过推迟构建图的其余部分来中断依赖关系图。最好的方法是将decorator更改为一个代理,并在其中注入一个工厂,该工厂将创建它应该包装的类型。这样的 handler.Handle(command)将如下所示:
// This class will be part of the Composition Root of
// the Windows Forms application
private class LifetimeScopeCommandHandlerProxy<T> : ICommandHandler<T>
{
// Since this type is part of the composition root,
// we are allowed to inject the container into it.
private Container container;
private Func<ICommandHandler<T>> factory;

public LifetimeScopeCommandHandlerProxy(Container container,
Func<ICommandHandler<T>> factory)
{
this.factory = factory;
this.container = container;
}

public void Handle(T command)
{
using (this.container.BeginLifetimeScope())
{
var handler = this.factory();

handler.Handle(command);
}
}
}

通过注入委托,我们可以延迟实例的创建时间,通过这样做,我们可以延迟依赖关系图(其余部分)的构建。现在的诀窍是以这样的方式注册这个代理类,它将注入包装好的实例,而不是(当然)再次注入自己。简单的注入器支持将 LifetimeScopeCommandHandlerProxy<T>工厂注入装饰器,因此您可以简单地使用 Func<T>方法,在这种情况下甚至可以使用 RegisterDecorator扩展方法。
注意,decorators(和这个代理)的注册顺序(显然)很重要。因为这个代理启动了一个新的生存期范围,所以它应该包装提交工作单元的decorator。换言之,更完整的注册将如下所示:
container.RegisterLifetimeScope<IUnitOfWork, UnitOfWork>();

container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());

// Register a decorator that handles saving the unit of
// work after a handler has executed successfully.
// This decorator will wrap all command handlers.
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>));

// Register the proxy that starts a lifetime scope.
// This proxy will wrap the transaction decorators.
container.RegisterSingleDecorator(
typeof(ICommandHandler<>),
typeof(LifetimeScopeCommandHandlerProxy<>));

以另一种方式注册代理和装饰器意味着 RegisterSingleDecorator将依赖于与依赖关系图的其他部分不同的 TransactionCommandHandlerDecorator<T>,这意味着对该关系图中的工作单元所做的所有更改都不会提交。换句话说,您的应用程序将停止工作。所以一定要仔细检查这个登记。
祝你好运。

关于c# - 简单注入(inject)器:跨相同图的服务注入(inject)相同的UnitOfWork实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10955077/

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