<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      CQRS實踐(3): Command執(zhí)行結(jié)果的返回

      上篇隨筆討論了CQRS中Command的一種基本實現(xiàn)。

      面對UI中的各種命令,Controller會創(chuàng)建相應(yīng)的Command對象,然后將其交給CommandBus,由CommandBus統(tǒng)一派發(fā)到相應(yīng)的CommandExecutor中去執(zhí)行,我們的ICommandBus的接口聲明如下:

      public interface ICommandBus
      {
      void Send<TCommand>(TCommand cmd) where TCommand : ICommand;
      }

      當(dāng)在實際項目中應(yīng)用CQRS時,我們會發(fā)現(xiàn)上面的做法存在一個問題:有時候我們希望Command在執(zhí)行完后返回一些結(jié)果,但上面的Send方法返回void,也就意味著我們沒有辦法得到執(zhí)行結(jié)果。我們以一個用戶注冊的例子來說明。

      數(shù)據(jù)庫中用戶表(User)的定義為User (Id, Email, NickName, Password),括號中的為字段,其中Id為varchar(36)的Guid字符串。

      注冊用戶的RegisterCommand代碼如下:

      public class RegisterCommand : ICommand
      {
      public string Email { get; set; }

      public string NickName { get; set; }

      public string Password { get; set; }

      public string ConfirmPassword { get; set; }

      public RegisterCommand()
      {
      }
      }

      假設(shè)在注冊后需要將新注冊用戶的Id存到Session中,那Controller的實現(xiàn)就變得有點糾結(jié):

      [HttpPost]
      public ActionResult Register(RegisterCommand command)
      {
      CommandBus.Send(command);

      Session["CurrentUserId"] = ???

      return Redirect("/");
      }

      上面的???處要怎么寫?我們無法直接得到新注冊用戶的Id,因為Id只有在Command執(zhí)行時才會生成。

      所以只能在CommandBus.Send(command)的下一行添加一個查詢:

      [HttpPost]
      public ActionResult Register(RegisterCommand command)
      {
      CommandBus.Send(command);

      var newUserId = Query<User>().OrderByDescending(u => u.Id).Select(u => u.Id).First();

      Session["CurrentUserId"] = newUserId;

      return Redirect("/");
      }

      這樣雖可以勉強(qiáng)解決問題,但比較繁瑣,而且也無法保證在Send之后查詢之前的這一小片時間里不會有其它新用戶產(chǎn)生。于是,我們開始反思Command的設(shè)計......

      解決方案1

      這個方案也正是Sharp Architecture所采用的,即添加一個帶兩個泛型參數(shù)的ICommandExecutor<TCommand, TResult>接口,這樣我們就有了兩個ICommandExecutor接口:

      public interface ICommandExecutor<TCommand>
      where TCommand : ICommand
      {
      void Execute(TCommand cmd);
      }

      // 這是新添加的帶有兩個泛型參數(shù)的接口
      public interface ICommandExecutor<TCommand, TResult>
      where TCommand : ICommand
      {
      // Execute方法返回值變成TResult
      TResult Execute(TCommand cmd);
      }

      為了適應(yīng)這種變化,我們也需要相應(yīng)修改ICommandBus的接口:

      public interface ICommandBus
      {
      void Send<TCommand>(TCommand cmd) where TCommand : ICommand;

      // 這個Send方法的返回值是TResult
      TResult Send<TCommand, TResult>(TCommand cmd) where TCommand : ICommand;
      }

      看起來不錯,現(xiàn)在對于注冊用戶的例子,我們只要調(diào)用第二個Send方法即可:

      [HttpPost]
      public ActionResult Register(RegisterCommand command)
      {
      var newUserId = CommandBus.Send<RegisterCommand, string>(command);

      Session["CurrentUserId"] = newUserId;

      return Redirect("/");
      }

      但如果我們仔細(xì)看看,會發(fā)現(xiàn)這是一個非常糟糕的設(shè)計!Controller的開發(fā)人員怎么知道RegisterCommand執(zhí)行完會返回結(jié)果?怎么知道返回的是string而不是int?Controller中可以寫成CommandBus.Send(command),也可以寫成CommandBus.Send<RegisterCommand, int>(command),也可以寫成CommandBus.Send<RegisterCommand, string>(command),同樣是發(fā)送RegisterCommand命令,這三種調(diào)用全都可以編譯通過,但是只有第三個才不會在運(yùn)行時出現(xiàn)問題。

      漸漸的,我們就會變成每調(diào)用一次CommandBus.Send()方法就要去查看對應(yīng)的CommandExecutor是怎么實現(xiàn)的,這就讓Command和CommandExecutor相分離的設(shè)計變得一點意義都沒有。所以我們需要尋求其它的解決方案。

      PS: Sharp Architecture中的ICommandHandler對應(yīng)本文中的ICommandExecutor,ICommandProcessor對應(yīng)本文中的ICommandBus,但我覺得它的ICommandProcessor的取名也太容易讓人誤解了,單從名字上看,誰能分清楚ICommandHandler和ICommandProcessor的區(qū)別?

      解決方案2

      其實這個方案非常簡單:在Command對象中添加一個ExecutionResult的屬性(這個屬性要放在具體的Command類中,不要放于ICommand接口中)。如上面的用戶注冊的例子,我們可以添加一個RegisterCommandResult的類,然后將RegisterCommand改成如下所示:

      public class RegisterCommand : ICommand
      {
      public string Email { get; set; }

      public string NickName { get; set; }

      public string Password { get; set; }

      public string ConfirmPassword { get; set; }

      // 亮點在這里
      public RegisterCommandResult ExecutionResult { get; set; }

      public RegisterCommand()
      {
      }
      }

      // 亮點在這里
      public class RegisterCommandResult
      {
      public string GeneratedUserId { get; set; }
      }

      在調(diào)用CommandBus.Send()之前,我們完全不用理會這個ExecutionResult屬性,對于Controller的開發(fā)人員來說,他只要知道在Command執(zhí)行完后,ExecutionResult的值就會被賦上,如果沒有,那就是CommandExecutor的bug。

      而我們的RegisterCommandExecutor就可以改成(User類的構(gòu)造函數(shù)會調(diào)用Id = Guid.NewGuid().ToString()對自己的Id進(jìn)行賦值):

      class RegisterCommandExecutor : ICommandExecutor<RegisterCommand>
      {
      public IRepository<User> _repository;

      public RegisterCommandExecutor(IRepository<User> repository)
      {
      _repository = repository;
      }

      public void Execute(RegisterCommand cmd)
      {
      var service = new RegistrationService(_repository);
      var user = service.Register(cmd.Email, cmd.NickName, cmd.Password);

      // 亮點在這里
      cmd.ExecutionResult = new RegisterCommandResult
      {
      GeneratedUserId = user.Id
      };
      }
      }

      然后Controller就很簡單了:

      [HttpPost]
      public ActionResult Register(RegisterCommand command)
      {
      CommandBus.Send(command);

      // 亮點在這里
      Session["CurrentUserId"] = command.ExecutionResult.GeneratedUserId;

      return Redirect("/");
      }

      這個方案和第一個方案的關(guān)鍵區(qū)別就在于,RegisterCommand中定義的ExecutionResult屬性可以讓開發(fā)人員清楚的知道這個屬性會在Command執(zhí)行完后被賦上合適的值。對于一個Command,如果開發(fā)人員在其中找到類似ExecutionResult這樣的屬性,他就知道這個Command執(zhí)行完后會返回執(zhí)行結(jié)果,并且結(jié)果是以賦值的形式賦給Command中的ExecutionResult屬性,若Command中沒有發(fā)現(xiàn)ExecutionResult這樣的屬性,那開發(fā)人員便知道這個Command執(zhí)行完不會返回執(zhí)行結(jié)果。

      PS: 因為本例中User.Id采用的是Guid字符串,它可以在創(chuàng)建User對象時立刻生成,所以下載中的代碼可以跑得還不錯,但如果User.Id是使用SQL Server的自增長int類型,那就跑不了了,因為UnitOfWork是在Command執(zhí)行完后才Commit的,所以,要處理自增Id的情況,我們需要稍微修改相應(yīng)代碼,比如將IUnitOfWork實例作為參數(shù)傳給ICommandExecutor.Execute()方法,并把IUnitOfWork的提交轉(zhuǎn)交給CommandExecutor負(fù)責(zé),然后在對RegisterCommand.ExecutionResult屬性賦值前先調(diào)用IUnitOfWork.Commit()方法,這樣便可以解決問題。

      到目前為止,我們所討論的Command都是同步執(zhí)行的,如果Command被設(shè)計為異步執(zhí)行,那本文所討論的內(nèi)容便可以直接忽略。

      如果系統(tǒng)的性能可以滿足需求,同步Command無疑是最好的。

      代碼下載

      https://files.cnblogs.com/mouhong-lin/CQRS-3.zip

      posted @ 2012-03-29 22:43  水言木  閱讀(7032)  評論(16)    收藏  舉報
      主站蜘蛛池模板: 国产精品日韩av在线播放| 性xxxx搡xxxxx搡欧美| 国产精品国产精品偷麻豆| 国产极品视频一区二区三区 | 18禁黄网站禁片免费观看| 少妇人妻偷人免费观看| 国产超碰无码最新上传| 一区二区三区AV波多野结衣| 久爱无码精品免费视频在线观看 | 欧美粗大| 国产高清无遮挡内容丰富| 最新中文字幕国产精品| 中文有无人妻VS无码人妻激烈 | 亚洲av无码牛牛影视在线二区| 国产高清在线不卡一区| 无码中文字幕日韩专区| 亚洲区激情区无码区日韩区| 亚洲精品一区二区天堂| 伊人蕉影院久亚洲高清| 97碰碰碰免费公开在线视频| 亚洲欧洲精品日韩av| 国产精品中文一区二区| 97国产成人无码精品久久久| 国内自拍偷拍一区二区三区| 国产熟睡乱子伦午夜视频| 东海县| 无码日韩精品一区二区人妻| 99中文字幕精品国产| 国产做a爱片久久毛片a片| 日本欧美一区二区三区在线播放| 成人无遮挡裸免费视频在线观看| 亚洲国产成人综合自在线| 久久99精品久久久久久9| 亚洲精品综合一区二区三区| 在线天堂新版资源www在线下载| 麻豆成人精品国产免费| 福鼎市| 夜夜嗨久久人成在日日夜夜| 欧美zoozzooz性欧美| 亚洲精品免费一二三区| 99久久婷婷国产综合精品青草漫画|