Nhibernate:谁负责非Web应用程序中的事务管理

Roo*_*ian 8 c# nhibernate data-access transactions data-access-layer

在Web应用程序中,一个工作单元负责事务管理.

但是Windows应用程序怎么样?

据我所知,存储库是我的数据访问层和业务层之间的连接器.它隐藏了我的业务层中的所有数据访问内容.

使用这个事实让我想到将所有事务内容放入存储库.

但我读到在存储库上使用Commit/RollBack方法违反了存储库的意图.

我问自己谁负责非Web应用程序中的事务管理,如何从业务层隐藏事务/ Nhibernate的东西?

def*_*mer 4

一般的答案是“无论谁实例化都ISession应该处理它。如果事务尚未提交,这实际上是回滚。”

我已经成功地使用命令模式来定义我想要对工作单元执行的操作。假设我们有一个Person实体,我们可以做的事情之一就是更改一个人的名字。让我们从实体开始:

public class Person
{
    public virtual int Id { get; private set; }
    public virtual string Name { get; private set; }

    public virtual void ChangeName(string newName)
    {
        if (string.IsNullOrWhiteSpace(newName))
        {
            throw new DomainException("Name cannot be empty");
        }

        if (newName.Length > 20)
        {
            throw new DomainException("Name cannot exceed 20 characters");
        }

        this.Name = newName;
    }
}
Run Code Online (Sandbox Code Playgroud)

定义一个简单的 POCO 命令,如下所示:

public class ChangeNameCommand : IDomainCommand
{
    public ChangeNameCommand(int personId, string newName)
    {
        this.PersonId = personId;
        this.NewName = newName;
    }

    public int PersonId { get; set; }
    public string NewName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

...以及命令的处理程序:

public class ChangeNameCommandHandler : IHandle<ChangeNameCommand>
{
    ISession session;

    public ChangeNameCommandHandler(ISession session)
    {
        // You could demand an IPersonRepository instead of using the session directly.
        this.session = session;
    }

    public void Handle(ChangeNameCommand command)
    {
        var person = session.Load<Person>(command.PersonId);
        person.ChangeName(command.NewName);
    }
}
Run Code Online (Sandbox Code Playgroud)

目标是存在于会话/工作范围之外的代码可以执行如下操作:

public class SomeClass
{
    ICommandInvoker invoker;

    public SomeClass(ICommandInvoker invoker)
    {
        this.invoker = invoker;
    }

    public void DoSomething()
    {
        var command = new ChangeNameCommand(1, "asdf");
        invoker.Invoke(command);
    }
}
Run Code Online (Sandbox Code Playgroud)

该命令的调用意味着“在工作单元上执行此命令”。这就是我们调用命令时希望发生的情况:

  1. 开始 IoC 嵌套作用域(“工作单元”作用域)
  2. 启动 ISession 和事务(这可能是步骤 3 的一部分)
  3. IHandle<ChangeNameCommand>从 IoC 范围解析 an
  4. 将命令传递给处理程序(域执行其工作)
  5. 提交交易
  6. 结束 IoC 范围(工作单元)

下面是一个使用Autofac作为 IoC 容器的示例:

public class UnitOfWorkInvoker : ICommandInvoker
{
    Autofac.ILifetimeScope scope;

    public UnitOfWorkInvoker(Autofac.ILifetimeScope scope)
    {
        this.scope = scope;
    }

    public void Invoke<TCommand>(TCommand command) where TCommand : IDomainCommand
    {
        using (var workScope = scope.BeginLifetimeScope("UnitOfWork")) // step 1
        {
            var handler = workScope.Resolve<IHandle<TCommand>>(); // step 3 (implies step 2)
            handler.Handle(command); // step 4

            var session = workScope.Resolve<NHibernate.ISession>();
            session.Transaction.Commit(); // step 5

        } // step 6 - When the "workScope" is disposed, Autofac will dispose the ISession.
          // If an exception was thrown before the commit, the transaction is rolled back.
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:UnitOfWorkInvoker我在这里展示的 违反了SRP - 它是 a UnitOfWorkFactory、 aUnitOfWork和 aInvoker多合一的。在我的实际实现中,我把它们分解了。