使用簡單的進樣器with the command pattern described here和the query pattern described here。對於其中一個命令,我有2個處理程序實現。第一個是「正常」的實現,同步執行:Simpleinjector:當我有2個實現並且想要選擇一個時,這是註冊ManyForOpenGeneric的正確方法嗎?
public class SendEmailMessageHandler
: IHandleCommands<SendEmailMessageCommand>
{
public SendEmailMessageHandler(IProcessQueries queryProcessor
, ISendMail mailSender
, ICommandEntities entities
, IUnitOfWork unitOfWork
, ILogExceptions exceptionLogger)
{
// save constructor args to private readonly fields
}
public void Handle(SendEmailMessageCommand command)
{
var emailMessageEntity = GetThisFromQueryProcessor(command);
var mailMessage = ConvertEntityToMailMessage(emailMessageEntity);
_mailSender.Send(mailMessage);
emailMessageEntity.SentOnUtc = DateTime.UtcNow;
_entities.Update(emailMessageEntity);
_unitOfWork.SaveChanges();
}
}
另一種是像這樣的命令裝飾,但明確地包裝了以前的類在一個單獨的線程來執行命令:
public class SendAsyncEmailMessageHandler
: IHandleCommands<SendEmailMessageCommand>
{
public SendAsyncEmailMessageHandler(ISendMail mailSender,
ILogExceptions exceptionLogger)
{
// save constructor args to private readonly fields
}
public void Handle(SendEmailMessageCommand command)
{
var program = new SendAsyncEmailMessageProgram
(command, _mailSender, _exceptionLogger);
var thread = new Thread(program.Launch);
thread.Start();
}
private class SendAsyncEmailMessageProgram
{
internal SendAsyncEmailMessageProgram(
SendEmailMessageCommand command
, ISendMail mailSender
, ILogExceptions exceptionLogger)
{
// save constructor args to private readonly fields
}
internal void Launch()
{
// get new instances of DbContext and query processor
var uow = MyServiceLocator.Current.GetService<IUnitOfWork>();
var qp = MyServiceLocator.Current.GetService<IProcessQueries>();
var handler = new SendEmailMessageHandler(qp, _mailSender,
uow as ICommandEntities, uow, _exceptionLogger);
handler.Handle(_command);
}
}
}
對於一陣簡單的噴射器對我大喊,告訴我它找到了2個實現IHandleCommands<SendEmailMessageCommand>
。我發現以下作品,但不確定它是否是最佳/最佳的方式。我想明確地登記這一個接口使用異步執行:
container.RegisterManyForOpenGeneric(typeof(IHandleCommands<>),
(type, implementations) =>
{
// register the async email handler
if (type == typeof(IHandleCommands<SendEmailMessageCommand>))
container.Register(type, implementations
.Single(i => i == typeof(SendAsyncEmailMessageHandler)));
else if (implementations.Length < 1)
throw new InvalidOperationException(string.Format(
"No implementations were found for type '{0}'.",
type.Name));
else if (implementations.Length > 1)
throw new InvalidOperationException(string.Format(
"{1} implementations were found for type '{0}'.",
type.Name, implementations.Length));
// register a single implementation (default behavior)
else
container.Register(type, implementations.Single());
}, assemblies);
我的問題:這是正確的方式,或者是有什麼好?例如,我想重用Simpleinjector爲所有其他實現拋出的現有異常,而不必在回調中顯式拋出它們。
更新回覆史蒂芬的答案
我已經更新了我的問題要更加明確。我以這種方式實現它的原因是,作爲操作的一部分,該命令在成功發送MailMessage
後更新db實體上名爲SentOnUtc
的System.Nullable<DateTime>
屬性。
的ICommandEntities
和IUnitOfWork
都由一個實體框架DbContext
class.The DbContext
每HTTP上下文註冊實現,使用the method described here:
container.RegisterPerWebRequest<MyDbContext>();
container.Register<IUnitOfWork>(container.GetInstance<MyDbContext>);
container.Register<IQueryEntities>(container.GetInstance<MyDbContext>);
container.Register<ICommandEntities>(container.GetInstance<MyDbContext>);
在simpleinjector維基RegisterPerWebRequest
擴展方法的默認行爲是當HttpContext
爲空(它將在新啓動的線程中)時註冊一個瞬態實例。
var context = HttpContext.Current;
if (context == null)
{
// No HttpContext: Let's create a transient object.
return _instanceCreator();
...
這就是爲什麼啓動方法使用Service Locator模式獲得的DbContext
一個實例,然後直接傳送到同步命令處理程序的構造函數。爲了使_entities.Update(emailMessageEntity)
和_unitOfWork.SaveChanges()
行能夠工作,兩者必須使用相同的DbContext實例。
注意:理想情況下,發送電子郵件應該由單獨的投票工作人員處理。這個命令基本上是一個隊列交換所。數據庫中的EmailMessage實體已經具有發送電子郵件所需的全部信息。這個命令只是從數據庫中抓取一個未發送的命令,發送它,然後記錄操作的DateTime。這樣的命令可以通過從不同的進程/應用進行輪詢來執行,但我不會接受這個問題的答案。現在,當某種類型的http請求事件觸發它時,我們需要啓動此命令。
看看[這個答案](http://stackoverflow.com/a/11899759/264697)。它談到了以更可靠的方式執行異步命令。 – Steven 2012-08-10 11:21:55