一般的な答えは、「ISession
をインスタンス化する人は誰でも処理してください。トランザクションがコミットされていない場合は、これは事実上ロールバックです」
私は、コマンドパターンを使用して、作業単位で実行したい操作を定義することで成功しました。 Person
エンティティがあり、私たちができることの1つは、人の名前を変更することです。
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 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 SomeClass
{
ICommandInvoker invoker;
public SomeClass(ICommandInvoker invoker)
{
this.invoker = invoker;
}
public void DoSomething()
{
var command = new ChangeNameCommand(1, "asdf");
invoker.Invoke(command);
}
}
このコマンドを呼び出すと、「作業ユニット上でこのコマンドを実行します。「これは、我々は、コマンドを呼び出すときに我々が起こることを望むものである:
- (IOCネストされたスコープを開始し、 『ワーク』スコープの単位)を
- スタートISessionとトランザクション(これはおそらくの一部として暗示されますステップ3)
- のIoC範囲から
IHandle<ChangeNameCommand>
- がハンドラにコマンドを渡し解決(ドメインがその作業を行う)
- は
- トランザクションをコミットするのIoCスコープ(作業単位)
を終了
は、だからここIoCコンテナとしてAutofacを使用した例です:
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
です。私の実際の実装では、私はそれらを壊しました。
Ayendeには、アプリケーションのhttps://github.com/ayende/Effectus –
があります。これは勝利フォームのためのソロソートかもしれません... Windowsサービスはどうですか? – Rookian
私は成功したパターンを記述することができます。これは、単純な「1トランザクションあたりのトランザクション」モデル(ネストされたトランザクションは不要)とIoCコンテナを使用している場合に適用されます。これはあなたのニーズを満たすでしょうか?基本的には、サービス層コードで「ドメイン作業を行う」と判断された場合、コマンドパターンとその呼び出し元(IoCによってサービスコードに呼び出されます) –