2011-07-26 79 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