Simple Injectorを使用して、注入された依存関係の存続期間を管理しています(この場合UnitOfWork
)。サービスやコマンドハンドラーではなく、別のデコレーターを使用して保存と破棄を行うことで、ビジネスロジックを作成する際のコードが非常に簡単になります。レイヤー(このブログ投稿で概説されているアーキテクチャに従います)。
上記は、Simple Injector MVC NuGetパッケージとコンポジションルートコンテナーの構築中に次のコードを使用することで完全に(そして非常に簡単に)機能します。グラフに複数の依存関係が存在する場合、同じインスタンスがすべてに注入されます。エンティティフレームワークモデルコンテキスト。
private static void InitializeContainer(Container container)
{
container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
// register all other interfaces with:
// container.Register<Interface, Implementation>();
}
ここで、いくつかのバックグラウンドスレッドを実行し、スレッドに関するSimple Injectorのドキュメントから、コマンドを次のようにプロキシできることを理解する必要があります。
public sealed class TransactionCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> handlerToCall;
private readonly IUnitOfWork unitOfWork;
public TransactionCommandHandlerDecorator(
IUnitOfWork unitOfWork,
ICommandHandler<TCommand> decorated)
{
this.handlerToCall = decorated;
this.unitOfWork = unitOfWork;
}
public void Handle(TCommand command)
{
this.handlerToCall.Handle(command);
unitOfWork.Save();
}
}
ThreadedCommandHandlerProxy:
public class ThreadedCommandHandlerProxy<TCommand>
: ICommandHandler<TCommand>
{
Func<ICommandHandler<TCommand>> instanceCreator;
public ThreadedCommandHandlerProxy(
Func<ICommandHandler<TCommand>> creator)
{
this.instanceCreator = creator;
}
public void Handle(TCommand command)
{
Task.Factory.StartNew(() =>
{
var handler = this.instanceCreator();
handler.Handle(command);
});
}
}
ただし、このスレッドのサンプルドキュメントから、ファクトリが使用されていることがわかります。ファクトリをコマンドとサービスレイヤーに導入すると、サービスごとに異なる保存方法があるため、混乱して一貫性がなくなります(1つのコンテナが保存を処理し、他のインスタンス化されたファクトリがサービスは保存と破棄を処理します)-ファクトリがない場合、サービスコードスケルトンがどれほど明確でシンプルであるかがわかります。
public class BusinessUnitCommandHandlers :
ICommandHandler<AddBusinessUnitCommand>,
ICommandHandler<DeleteBusinessUnitCommand>
{
private IBusinessUnitService businessUnitService;
private IInvoiceService invoiceService;
public BusinessUnitCommandHandlers(
IBusinessUnitService businessUnitService,
IInvoiceService invoiceService)
{
this.businessUnitService = businessUnitService;
this.invoiceService = invoiceService;
}
public void Handle(AddBusinessUnitCommand command)
{
businessUnitService.AddCompany(command.name);
}
public void Handle(DeleteBusinessUnitCommand command)
{
invoiceService.DeleteAllInvoicesForCompany(command.ID);
businessUnitService.DeleteCompany(command.ID);
}
}
public class BusinessUnitService : IBusinessUnitService
{
private readonly IUnitOfWork unitOfWork;
private readonly ILogger logger;
public BusinessUnitService(IUnitOfWork unitOfWork,
ILogger logger)
{
this.unitOfWork = unitOfWork;
this.logger = logger;
}
void IBusinessUnitService.AddCompany(string name)
{
// snip... let container call IUnitOfWork.Save()
}
void IBusinessUnitService.DeleteCompany(int ID)
{
// snip... let container call IUnitOfWork.Save()
}
}
public class InvoiceService : IInvoiceService
{
private readonly IUnitOfWork unitOfWork;
private readonly ILogger logger;
public BusinessUnitService(IUnitOfWork unitOfWork,
ILogger logger)
{
this.unitOfWork = unitOfWork;
this.logger = logger;
}
void IInvoiceService.DeleteAllInvoicesForCompany(int ID)
{
// snip... let container call IUnitOfWork.Save()
}
}
上記の問題が発生し始め、ASP .NET PerWebRequestの有効期間に関するドキュメントから理解できるように、次のコードが使用されます。
public T GetInstance()
{
var context = HttpContext.Current;
if (context == null)
{
// No HttpContext: Let's create a transient object.
return this.instanceCreator();
}
object key = this.GetType();
T instance = (T)context.Items[key];
if (instance == null)
{
context.Items[key] = instance = this.instanceCreator();
}
return instance;
}
上記はHTTPリクエストごとに正常に機能しますが、有効なものがありますHttpContext.Current
が、それを使用して新しいスレッドをスピンアップすると、新しいスレッドThreadedCommandHandlerProxy
が作成さHttpContext
れ、そのスレッド内に存在しなくなります。
後続の呼び出しごとにはnullになるため、HttpContext
サービスコンストラクターに注入されたオブジェクトのすべてのインスタンスは新しく一意になります。これは、オブジェクトがすべてのサービスで同じインスタンスとして正しく共有されるWebリクエストごとの通常のHTTPとは異なります。
したがって、上記を質問に要約すると、次のようになります。
HTTPリクエストから作成されたか、新しいスレッドを介して作成されたかに関係なく、オブジェクトを構築して共通アイテムを注入するにはどうすればよいですか?
UnitOfWork
コマンドハンドラプロキシ内のスレッドによって管理されるための特別な考慮事項はありますか?ハンドラーの実行後に確実に保存および破棄するにはどうすればよいですか?
command-handler / service-layer内で問題が発生し、を保存したくない場合はUnitOfWork
、単に例外をスローしますか?もしそうなら、これをグローバルレベルでキャッチすることは可能ですか、それともハンドラデコレータまたはプロキシのtry
-内からリクエストごとに例外をキャッチする必要がありますか?catch
ありがとう、
クリス