在web应用程序中,一个工作单元负责事务管理。
但是windows应用程序呢?
据我所知,存储库是数据访问层和业务层之间的连接器。它从我的业务层隐藏了所有数据访问的东西。
使用这个事实让我想到将所有事务的东西放入存储库。
但是我读到在存储库上使用Commit/RollBack方法违反了存储库的意图。
我问自己谁负责非web应用程序中的事务管理,我如何从业务层隐藏事务/Nhibernate的东西?
一般的答案是"实例化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;
}
}
像这样定义一个简单的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; }
}
…和用于命令的Handler:
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);
}
}
目标是存在于Session/Work作用域之外的代码可以做这样的事情:
public class SomeClass
{
ICommandInvoker invoker;
public SomeClass(ICommandInvoker invoker)
{
this.invoker = invoker;
}
public void DoSomething()
{
var command = new ChangeNameCommand(1, "asdf");
invoker.Invoke(command);
}
}
命令的调用意味着"在一个工作单元上执行该命令"。这是我们在调用命令
时希望发生的情况:- 开始IoC嵌套作用域("Unit of Work"作用域)
- 启动会话和事务(这可能隐含为步骤3的一部分)
- 从IoC范围内解决
IHandle<ChangeNameCommand>
- 将命令传递给处理程序(域执行其工作)
- 提交事务
- 结束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.
}
}
注意:我在这里展示的UnitOfWorkInvoker
违反了SRP -它是一个UnitOfWorkFactory
,一个UnitOfWork
和一个Invoker
。在我的实际实现中,我把它们分开了。
当我使用存储库时,它们包含在一个工作单元中。工作单元跟踪对存储库的更改并处理事务管理。
为什么在web应用程序中使用工作单元来处理事务管理是有效的,而不是在windows应用程序中?如果它是一个n层应用程序,您的业务层实际上将在两者之间共享。