【问题标题】:How to share instance between decoratee and decorator in the context of ScopedLifestyle.Flowing如何在 ScopedLifestyle.Flowing 的上下文中在被装饰者和装饰者之间共享实例
【发布时间】:2020-10-30 02:22:46
【问题描述】:

我不明白如何使用 DI 容器在装饰者和装饰者之间共享实例。

以下示例说明了我的问题。 context 实例在TransactionCommandDecoratorCommand 服务之间共享。

var context = UowFactory.GetUnitOfWork();

var command = new TransactionCommandDecorator(
    context,
    new Command(context));
    
command.Execute(new CommandParams { });

context.dispose();

基本上,我需要有很多与数据库交互并调用存储库的命令。然后我想通过使用包装命令服务的装饰器来应用事务。

问题是我不知道如何在装饰者和被装饰者之间共享上下文(如示例中所示),因为每次执行命令时我都需要一个新的 DbContext 实例。

您能否解释一下我如何通过在 Scope 流动的上下文中使用 Simple Injector 来使其工作 (ScopedLifestyle.Flowing)。

装饰器和装饰器实现的一个可能示例

命令示例:

public Command(IUnitOfWork uow) => _uow = uow;

public DbResult Execute(CommandParams args)
{
    _uow.Repo1.Add(args.Item);
    _uow.Repo1.Add(args.Item2);
    _uow.Repo1.Remove(args.Item3);
}

事务命令装饰器:

public class TransactionCommandDecorator : ICommand
{
    public TransactionCommandDecorator(IUnitOfWork uow, ICommand command)
    {
        _uow = uow;
        _command = command;
    }

    public DbResult Execute(commandParams args)
    {
        try
        {
            var transaction = _uow.GetContext().Database.GetTransaction();
            var ret = _command.Execute(args);
            
            if (!ret.Success)
            {
                transaction.Discard();
                return;
            }
            
            transaction.Commit();
        }
        catch
        {
            transaction.Discard();
        }
        finally
        {
            transaction.Dispose();
        }
    }
}

【问题讨论】:

    标签: c# dependency-injection command inversion-of-control simple-injector


    【解决方案1】:

    IUnitOfWork 可以在具有相同Scope 的类之间共享,方法是将其注册为Lifestyle.Scoped,如下例所示:

    container.Register<IUnitOfWork>(() => UowFactory.GetUnitOfWork(), Lifestyle.Scoped);
    container.Register<ICommand, Command>();
    container.RegisterDecorator<ICommand, TransactionCommandDecorator>();
    

    用法(使用 ScopedLifestyle.Flowing):

    using (var scope = new Scope(container))
    {
        ICommand command = scope.GetInstance<ICommand>();
    
        command.Execute(new CommandParams { });
    }
    

    用法(使用环境范围):

    using (AsyncScopedLifestyle.BeginScope(container))
    {
        ICommand command = container.GetInstance<ICommand>();
    
        command.Execute(new CommandParams { });
    }
    

    【讨论】:

    • 感谢@Steven,我开始更好地理解类的生命周期以及容器的使用。我认为我需要在如何安排类架构上转变思路,遗憾的是,目前这对我来说有点晦涩难懂。因此,据我所知……每次一个类引用 IUnitOfWork 时,它都需要由 ScopedCommandDecorator 包装……它是正确的吗?
    • @FedericoBorghesi 这取决于。在您的 WPF 应用程序中,视图和视图模型可以存在很长时间,这将导致它们的依赖关系也存在。这对于诸如DbContext 之类的依赖项可能会有问题。在这种情况下,一个好的解决方案是将业务操作包装在应用范围的装饰器中。但是,当您需要围绕许多类的装饰器时,最好让您的装饰器通用,您可能已经在这样做了。
    • 是的,我确实想这样做,但在我的情况下,我有每个实现 IDatabaseCommand 的具体命令?我如何为此制作通用装饰器?在我的体系结构中,我需要为特定 IDatabaseCommand 的每个实现都有一个装饰器?例如 IDatabaseCommand 有他的作用域装饰器,而 IDatabaseCommand 也有他的装饰器?喜欢this example
    • 在上一个问题中,您提到了 Simple Injector 文档的 AOP 部分。该部分包含大量有关通用装饰器的示例。
    • 非常宝贵的帮助再次感谢。我现在开始考虑你在之前的帖子中关于 AOP 的另一个回复。再次对我有时愚蠢的问题感到抱歉,但我试着在我的正常工作和业余时间之间研究这个,所以有时我会错过一些东西。我现在访问文档中的 AOP 部分,但可能是文档站点已关闭?
    猜你喜欢
    • 1970-01-01
    • 2020-04-07
    • 1970-01-01
    • 2013-03-03
    • 1970-01-01
    • 1970-01-01
    • 2019-02-03
    相关资源
    最近更新 更多