那麼在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提供給服務代碼) –