2011-07-26 119 views
8

那么在Web应用程序中,工作单元负责事务管理。Nhibernate:谁负责非Web应用程序中的事务管理

但是,Windows应用程序呢?

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

使用这个事实让我想到把所有的事务处理的东西放入存储库。

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

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

+3

Ayende有一个示例应用程序https://github.com/ayende/Effectus –

+0

好吧,这可能是一个赢取形式solotion ...那么Windows服务呢? – Rookian

+0

我可以描述一个我已经成功的模式。如果您有一个简单的“每个操作一个事务”模型(不需要嵌套事务)并且您正在使用IoC容器,则它适用。这会满足您的需求吗?基本上,当服务层代码决定是“做域名工作”的时候,它使用命令模式和命令的调用者(调用者由IoC提供给服务代码) –

回答

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; 
    } 
} 

定义一个简单的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); 
    } 
} 

命令的调用意味着“在一个工作单元上执行此命令。“这是我们希望发生的时候,我们调用命令:

  1. 开始一个IoC嵌套的范围(以下简称‘人的工作’范围单元)
  2. 开始一个ISession和交易(这可能是暗示的组成部分步骤3)
  3. 从IOC范围
  4. 解决一个IHandle<ChangeNameCommand>传递命令处理程序(域执行其工作)
  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. 
    } 
} 

注:UnitOfWorkInvoker我这里显示违反SRP - 这是一个UnitOfWorkFactory,一个UnitOfWorkInvoker都在同一个。在我的实际实施中,我把它们分开了。

+0

谁负责调用session.SaveOrUpdate?谁负责调用session.Save或session.Update以防万一你已经分配了Ids?我也不知道是否最好为每个命令提交事务。在Web应用程序中,通常每个请求都有一个会话和事务(最好是延迟加载/启动),如果发生任何错误,则在请求结束时提交或回滚。既然交易是由创建会话的人开始的,那么提交或回滚也是它的责任,或者我错了吗? – Loudenvier

+0

@Loudenvier - 根据应用程序,“每个命令一个事务”模式可能不合适。命令只是划定交易界限的一种可能方式。在我的示例中,UnitOfWorkInvoker通过调用'Resolve'隐式创建会话和事务,因此它可以提交它。此外,懒惰可以很容易地添加(假设Autofac)通过向DI容器询问'懒惰'。 –

+0

我也使用懒惰,但采用更简单的方法,因为它是针对Web应用程序量身定制的。我相信它的功能与您自己的非常相似,但它不太复杂,因为我现在不需要它。 我真的不明白您的示例是Save或SaveOrUpdate将被调用。 ChangeNameCommandHandler只调用person.ChangeName(),但它不保存,更新或SaveOrUpdate它...所以我想知道你在哪里跟踪更改/创建的实体并调用save ...也许命令本身应该这样做,对吧? – Loudenvier

1

当我使用存储库时,它们包含在一个工作单元中。工作单元跟踪存储库的变化并处理事务管理。

为什么使用工作单元来处理Web应用程序中的事务管理而不是Windows应用程序是有效的?如果它是一个N层应用程序,那么您的业务层实际上将在两者之间共享。

+0

我同意乔尔在这里。我不明白为什么它会有所不同。目前我正在使用存储库,并将IUnitOfWork \ ISession传递到每个存储库。 –

+0

IUnitOfWork进入存储库Oo ?! – Rookian

+0

以及存储库和工作单元取决于ISession/IStatelessSession。在Web中共享HttpContext中的会话。在非web应用程序中,您必须将会话放入自己的字典中。是的,现在我比目鱼... – Rookian