那么在Web应用程序中,工作单元负责事务管理。Nhibernate:谁负责非Web应用程序中的事务管理
但是,Windows应用程序呢?
据我所知,存储库是我的数据访问层和业务层之间的连接器。 它隐藏了我业务层的所有数据访问内容。
使用这个事实让我想到把所有的事务处理的东西放入存储库。
但我读到在存储库上有Commit/RollBack方法违反了存储库的意图。
我问自己谁负责非Web应用程序中的事务管理,以及如何从业务层隐藏事务/ Nhibernate的东西?
那么在Web应用程序中,工作单元负责事务管理。Nhibernate:谁负责非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; }
}
...和该命令处理程序:
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);
}
}
的目标是,代码存在于会话/工作范围外可以这样做:
public class SomeClass
{
ICommandInvoker invoker;
public SomeClass(ICommandInvoker invoker)
{
this.invoker = invoker;
}
public void DoSomething()
{
var command = new ChangeNameCommand(1, "asdf");
invoker.Invoke(command);
}
}
命令的调用意味着“在一个工作单元上执行此命令。“这是我们希望发生的时候,我们调用命令:
IHandle<ChangeNameCommand>
传递命令处理程序(域执行其工作)因此,这里是使用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
都在同一个。在我的实际实施中,我把它们分开了。
谁负责调用session.SaveOrUpdate?谁负责调用session.Save或session.Update以防万一你已经分配了Ids?我也不知道是否最好为每个命令提交事务。在Web应用程序中,通常每个请求都有一个会话和事务(最好是延迟加载/启动),如果发生任何错误,则在请求结束时提交或回滚。既然交易是由创建会话的人开始的,那么提交或回滚也是它的责任,或者我错了吗? – Loudenvier
@Loudenvier - 根据应用程序,“每个命令一个事务”模式可能不合适。命令只是划定交易界限的一种可能方式。在我的示例中,UnitOfWorkInvoker通过调用'Resolve'隐式创建会话和事务,因此它可以提交它。此外,懒惰可以很容易地添加(假设Autofac)通过向DI容器询问'懒惰
我也使用懒惰
当我使用存储库时,它们包含在一个工作单元中。工作单元跟踪存储库的变化并处理事务管理。
为什么使用工作单元来处理Web应用程序中的事务管理而不是Windows应用程序是有效的?如果它是一个N层应用程序,那么您的业务层实际上将在两者之间共享。
Ayende有一个示例应用程序https://github.com/ayende/Effectus –
好吧,这可能是一个赢取形式solotion ...那么Windows服务呢? – Rookian
我可以描述一个我已经成功的模式。如果您有一个简单的“每个操作一个事务”模型(不需要嵌套事务)并且您正在使用IoC容器,则它适用。这会满足您的需求吗?基本上,当服务层代码决定是“做域名工作”的时候,它使用命令模式和命令的调用者(调用者由IoC提供给服务代码) –