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

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

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

      [開源]OSharpNS 步步為營系列 - 3. 添加業(yè)務(wù)服務(wù)層

      什么是OSharp

      OSharpNS全稱OSharp Framework with .NetStandard2.0,是一個(gè)基于.NetStandard2.0開發(fā)的一個(gè).NetCore快速開發(fā)框架。這個(gè)框架使用最新穩(wěn)定版的.NetCore SDK(當(dāng)前是.NET Core 2.2),對 AspNetCore 的配置、依賴注入、日志、緩存、實(shí)體框架、Mvc(WebApi)、身份認(rèn)證、權(quán)限授權(quán)等模塊進(jìn)行更高一級的自動化封裝,并規(guī)范了一套業(yè)務(wù)實(shí)現(xiàn)的代碼結(jié)構(gòu)與操作流程,使 .Net Core 框架更易于應(yīng)用到實(shí)際項(xiàng)目開發(fā)中。

      概述

      一個(gè)模塊的服務(wù)層,主要負(fù)責(zé)如下幾個(gè)方面的工作:

      • 向 API層 提供各個(gè)實(shí)體的數(shù)據(jù)查詢的 IQueryable<T> 類型的數(shù)據(jù)源
      • 接收 API層 傳入的 IInputDto 參數(shù),完成實(shí)體的 新增更新刪除 等業(yè)務(wù)操作
      • 接收 API層 傳入的參數(shù),處理各種 模塊級別 的綜合業(yè)務(wù)
      • 處理完業(yè)務(wù)之后,將數(shù)據(jù)通過 數(shù)據(jù)倉儲IRepository 更新到數(shù)據(jù)庫
      • 事件總線 模塊發(fā)布業(yè)務(wù)處理事件,觸發(fā)訂閱的業(yè)務(wù)事件
      • 向 API 層返回業(yè)務(wù)操作結(jié)果

      整個(gè)過程如下圖所示:

      服務(wù)層代碼布局

      服務(wù)層代碼布局分析

      一個(gè)業(yè)務(wù)模塊,是負(fù)責(zé)完成一系列功能的,這些功能相互之間具有密切的關(guān)聯(lián)性,所以對于一個(gè)模塊來說,業(yè)務(wù)服務(wù)是一個(gè)整體,不應(yīng)把他們再按單個(gè)實(shí)體拆分開來。
      OSharp 的業(yè)務(wù)模塊代碼結(jié)構(gòu)設(shè)計(jì),也是根據(jù)這一原則來設(shè)計(jì)的。設(shè)計(jì)規(guī)則如下:

      • 服務(wù)接口IBlogsContract:一個(gè)模塊的業(yè)務(wù)服務(wù)共享一個(gè)服務(wù)接口,接口中包含模塊的綜合業(yè)務(wù)服務(wù),也包含模塊的各個(gè)實(shí)體的查詢數(shù)據(jù)集、新增、更新、刪除等自有業(yè)務(wù)服務(wù)。

      • 服務(wù)實(shí)現(xiàn)BlogsService:服務(wù)實(shí)現(xiàn)使用 分部類partial 設(shè)計(jì),例如本例中的博客模塊業(yè)務(wù),文件拆分如下:

        • BlogsService.cs:博客模塊服務(wù)實(shí)現(xiàn)類的主文件,負(fù)責(zé)各實(shí)體的倉儲服務(wù)注入,輔助服務(wù)注入,模塊綜合業(yè)務(wù)實(shí)現(xiàn)
        • BlogsService.Blog.cs:博客模塊服務(wù)的博客實(shí)體服務(wù)實(shí)現(xiàn)類,負(fù)責(zé)博客實(shí)體的 查詢數(shù)據(jù)集、增改刪 業(yè)務(wù)實(shí)現(xiàn)
        • BlogsService.Post.cs:博客模塊服務(wù)的文章實(shí)體服務(wù)實(shí)現(xiàn)類,負(fù)責(zé)文章實(shí)體的 查詢數(shù)據(jù)集、增改刪 業(yè)務(wù)實(shí)現(xiàn)
      • 模塊入口BlogsPack:定義模塊的級別、啟動順序、執(zhí)行服務(wù)添加、模塊初始化等功能

      綜上,服務(wù)層代碼布局如下所示:

      src                                         # 源代碼文件夾
      └─Liuliu.Blogs.Core                         # 項(xiàng)目核心工程
         └─Blogs                                  # 博客模塊文件夾
              ├─Events                            # 業(yè)務(wù)事件文件夾
              │    ├─VerifyBlogEventData.cs       # 審核博客事件數(shù)據(jù)
              │    └─VerifyBlogEventHandler.cs    # 審核博客事件處理器
              ├─BlogsPack.cs                      # 博客模塊入口類
              ├─BlogsService.cs                   # 博客服務(wù)類
              ├─BlogsService.Blog.cs              # 博客模塊-博客服務(wù)類
              ├─BlogsService.Post.cs              # 博客模塊-文章服務(wù)類
              └─IBlogsContract.cs                 # 博客模塊服務(wù)接口
      

      服務(wù)接口 IBlogsContract

      接口定義分析

      數(shù)據(jù)查詢

      對于數(shù)據(jù)查詢,業(yè)務(wù)層只向 API 層開放一個(gè) IQueryable<TEntity> 的查詢數(shù)據(jù)集。原則上,服務(wù)層不實(shí)現(xiàn) 純數(shù)據(jù)查詢(例如 用于列表分頁數(shù)據(jù)、下拉菜單選項(xiàng) 等數(shù)據(jù),不涉及數(shù)據(jù)變更的查詢操作) 的服務(wù),所有的 純數(shù)據(jù)查詢 都在 API層 按需要進(jìn)行查詢。具體分析請看 >>數(shù)據(jù)查詢應(yīng)該在哪做>>

      額外的,根據(jù)一定條件判斷一個(gè)數(shù)據(jù)是否存在 這種需求經(jīng)常會用到(例如在新增或修改一個(gè)要求唯一的字符串時(shí),需要異步檢查輸入的字符串是否已存在),因此設(shè)計(jì)一個(gè) 檢查實(shí)體是否存在CheckEntityExists 的服務(wù)很有必要。

      !!!node
      對于新增、更新、刪除操作,除非很確定一次只操作一條記錄除外,為了支持可能的批量操作,設(shè)計(jì)上都應(yīng)把服務(wù)層的 增改刪 操作設(shè)計(jì)為數(shù)組型參數(shù)的批量操作,同時(shí)使用 params 關(guān)鍵字使操作支持單個(gè)數(shù)據(jù)操作。

      數(shù)據(jù)變更

      對于每一個(gè)實(shí)體,服務(wù)層按 業(yè)務(wù)需求分析 的要求定義必要的 新增、更新、刪除 等操作,OSharp框架定義了一個(gè) 業(yè)務(wù)操作結(jié)果信息類 OperationResult 來封裝業(yè)務(wù)操作結(jié)果,這個(gè)結(jié)果可以返回 操作結(jié)果類型(成功/錯(cuò)誤/未變化/不存在/驗(yàn)證失敗)、返回消息、返回附加數(shù)據(jù) 等豐富的信息,API層 接受操作結(jié)果后可進(jìn)行相應(yīng)的處理。

      博客模塊的接口定義

      回到我們的 Liuliu.Blogs 項(xiàng)目,根據(jù) <業(yè)務(wù)模塊設(shè)計(jì)#服務(wù)層> 的需求分析,我們需要給 博客Blog 實(shí)體定義 申請開通、開通審核、更新、刪除 服務(wù),給 文章Post 實(shí)體類定義 新增、更新、刪除 服務(wù)。

      接口定義如下:

      
      /// <summary>
      /// 業(yè)務(wù)契約接口:博客模塊
      /// </summary>
      public interface IBlogsContract
      {
          #region 博客信息業(yè)務(wù)
      
          /// <summary>
          /// 獲取 博客信息查詢數(shù)據(jù)集
          /// </summary>
          IQueryable<Blog> Blogs { get; }
      
          /// <summary>
          /// 檢查博客信息是否存在
          /// </summary>
          /// <param name="predicate">檢查謂語表達(dá)式</param>
          /// <param name="id">更新的博客信息編號</param>
          /// <returns>博客信息是否存在</returns>
          Task<bool> CheckBlogExists(Expression<Func<Blog, bool>> predicate, int id = 0);
      
          /// <summary>
          /// 申請博客信息
          /// </summary>
          /// <param name="dto">申請博客信息DTO信息</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          Task<OperationResult> ApplyForBlog(BlogInputDto dto);
      
          /// <summary>
          /// 審核博客信息
          /// </summary>
          /// <param name="id">博客編號</param>
          /// <param name="isEnabled">是否通過</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          Task<OperationResult> VerifyBlog(int id, bool isEnabled);
      
          /// <summary>
          /// 更新博客信息
          /// </summary>
          /// <param name="dtos">包含更新信息的博客信息DTO信息</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          Task<OperationResult> UpdateBlogs(params BlogInputDto[] dtos);
      
          /// <summary>
          /// 刪除博客信息
          /// </summary>
          /// <param name="ids">要?jiǎng)h除的博客信息編號</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          Task<OperationResult> DeleteBlogs(params int[] ids);
      
          #endregion
      
          #region 文章信息業(yè)務(wù)
      
          /// <summary>
          /// 獲取 文章信息查詢數(shù)據(jù)集
          /// </summary>
          IQueryable<Post> Posts { get; }
      
          /// <summary>
          /// 檢查文章信息是否存在
          /// </summary>
          /// <param name="predicate">檢查謂語表達(dá)式</param>
          /// <param name="id">更新的文章信息編號</param>
          /// <returns>文章信息是否存在</returns>
          Task<bool> CheckPostExists(Expression<Func<Post, bool>> predicate, int id = 0);
      
          /// <summary>
          /// 添加文章信息
          /// </summary>
          /// <param name="dtos">要添加的文章信息DTO信息</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          Task<OperationResult> CreatePosts(params PostInputDto[] dtos);
      
          /// <summary>
          /// 更新文章信息
          /// </summary>
          /// <param name="dtos">包含更新信息的文章信息DTO信息</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          Task<OperationResult> UpdatePosts(params PostInputDto[] dtos);
      
          /// <summary>
          /// 刪除文章信息
          /// </summary>
          /// <param name="ids">要?jiǎng)h除的文章信息編號</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          Task<OperationResult> DeletePosts(params int[] ids);
      
          #endregion
      }
      
      

      服務(wù)實(shí)現(xiàn) BlogsService

      依賴服務(wù)注入方式分析

      服務(wù)層的業(yè)務(wù)實(shí)現(xiàn),通過向服務(wù)實(shí)現(xiàn)類注入 數(shù)據(jù)倉儲IRepository<TEntity, TKey> 對象來獲得向數(shù)據(jù)庫存取數(shù)據(jù)的能力。根據(jù) .NetCore 的依賴注入使用原則,常規(guī)的做法是在服務(wù)實(shí)現(xiàn)類的 構(gòu)造函數(shù) 進(jìn)行依賴服務(wù)的注入。形如:

      
      /// <summary>
      /// 業(yè)務(wù)服務(wù)實(shí)現(xiàn):博客模塊
      /// </summary>
      public class BlogsService : IBlogsContract
      {
          private readonly IRepository<Blog, int> _blogRepository;
          private readonly IRepository<Post, int> _postRepository;
          private readonly IRepository<User, int> _userRepository;
          private readonly IRepository<Role, int> _roleRepository;
          private readonly IRepository<UserRole, Guid> _userRoleRepository;
          private readonly IEventBus _eventBus;
      
          /// <summary>
          /// 初始化一個(gè)<see cref="BlogsService"/>類型的新實(shí)例
          /// </summary>
          public BlogsService(IRepository<Blog, int> blogRepository,
              IRepository<Post, int> postRepository,
              IRepository<User, int> userRepository,
              IRepository<Role, int> roleRepository,
              IRepository<UserRole, Guid> userRoleRepository,
              IEventBus eventBus)
          {
              _blogRepository = blogRepository;
              _postRepository = postRepository;
              _userRepository = userRepository;
              _roleRepository = roleRepository;
              _userRoleRepository = userRoleRepository;
              _eventBus = eventBus;
          }
      }
      
      

      構(gòu)造函數(shù)注入帶來的性能影響

      每個(gè)倉儲都使用構(gòu)造函數(shù)注入的話,如果模塊的業(yè)務(wù)比較復(fù)雜,涉及的實(shí)體比較多(比如十幾個(gè)實(shí)體是很經(jīng)常的事),就會造成每次實(shí)例化 BlogsService 類的實(shí)例的時(shí)候,都需要去實(shí)例化很多個(gè)依賴服務(wù),而實(shí)際上 一次業(yè)務(wù)執(zhí)行只執(zhí)行服務(wù)中的某個(gè)方法,可能也就用到其中的一兩個(gè)依賴服務(wù),這就造成了很多不必要的額外工作,也就是性能損耗。

      依賴服務(wù)注入的性能優(yōu)化

      如果 不考慮業(yè)務(wù)服務(wù)的可測試性(單元測試通常需要Mock依賴服務(wù))的話,在構(gòu)造函數(shù)中只注入 IServiceProvider 實(shí)例,然后在業(yè)務(wù)代碼中使用 serviceProvider.GetService<T>() 的方式來 按需獲取 依賴服務(wù)的實(shí)例,是比較經(jīng)濟(jì)的方式。則服務(wù)實(shí)現(xiàn)變?yōu)槿缦滤荆?/p>

      /// <summary>
      /// 業(yè)務(wù)服務(wù)實(shí)現(xiàn):博客模塊
      /// </summary>
      public class BlogsService : IBlogsContract
      {
          private readonly IServiceProvider _serviceProvider;
      
          /// <summary>
          /// 初始化一個(gè)<see cref="BlogsService"/>類型的新實(shí)例
          /// </summary>
          public BlogsService(IServiceProvider serviceProvider)
          {
              _serviceProvider = serviceProvider;
          }
      
          /// <summary>
          /// 獲取 博客倉儲對象
          /// </summary>
          protected IRepository<Blog, int> BlogRepository => _serviceProvider.GetService<IRepository<Blog, int>>();
      
          /// <summary>
          /// 獲取 文章倉儲對象
          /// </summary>
          protected IRepository<Post, int> PostRepository => _serviceProvider.GetService<IRepository<Post, int>>();
      
          /// <summary>
          /// 獲取 用戶倉儲對象
          /// </summary>
          protected IRepository<User, int> UserRepository => _serviceProvider.GetService<IRepository<User, int>>();
      
          /// <summary>
          /// 獲取 角色倉儲對象
          /// </summary>
          protected IRepository<Role, int> RoleRepository => _serviceProvider.GetService<IRepository<Role, int>>();
      
          /// <summary>
          /// 獲取 角色倉儲對象
          /// </summary>
          protected IRepository<UserRole, Guid> UserRoleRepository => _serviceProvider.GetService<IRepository<UserRole, Guid>>();
      
          /// <summary>
          /// 獲取 事件總線對象
          /// </summary>
          protected IEventBus EventBus => _serviceProvider.GetService<IEventBus>();
      }
      

      各個(gè)依賴服務(wù)改為屬性的存在方式,并且可訪問性為 protected,這就保證了依賴服務(wù)的安全性。依賴服務(wù)使用 serviceProvider.GetService<T>() 的方式創(chuàng)建實(shí)例,可以做到 按需創(chuàng)建,達(dá)到性能優(yōu)化的目的。

      增改刪操作的簡化

      常規(guī)批量操作的弊端

      直接通過 數(shù)據(jù)倉儲IRepository<TEntity, TKey> 實(shí)現(xiàn)數(shù)據(jù)的增改刪的批量操作,總免不了要使用循環(huán)來遍歷傳進(jìn)來的多個(gè)InputDto,例如文章的更新操作,以下幾個(gè)步驟是免不了的:

      1. dto.Id 查找出相應(yīng)的文章實(shí)體 entity,如果不存在,中止操作并返回
      2. 進(jìn)行更新前的數(shù)據(jù)檢查
        1. 檢查 dto 的合法性,比如文章標(biāo)題要求唯一,dto.Title 就要驗(yàn)證唯一性,中止操作并返回
        2. 檢查 entity 的合法性,比如文章已鎖定,就不允許編輯,要進(jìn)行攔截,檢查不通過,中止操作并返回
      3. 使用 AutoMapperdto 的值更新到 entity
      4. 進(jìn)行其他關(guān)聯(lián)實(shí)體的更新
        1. 比如添加文章的編輯記錄
        2. 比如給當(dāng)前操作人加積分
      5. entity 的更新提交到數(shù)據(jù)庫

      整個(gè)過程實(shí)現(xiàn)代碼如下:

      /// <summary>
      /// 更新文章信息
      /// </summary>
      /// <param name="dtos">包含更新信息的文章信息DTO信息</param>
      /// <returns>業(yè)務(wù)操作結(jié)果</returns>
      public virtual async Task<OperationResult> UpdatePosts(params PostInputDto[] dtos)
      {
          Check.Validate<PostInputDto, int>(dtos, nameof(dtos));
      
          int count = 0;
          foreach (PostInputDto dto in dtos)
          {
              Post entity = await PostRepository.GetAsync(dto.Id);
              if (entity == null)
              {
                  return new OperationResult(OperationResultType.QueryNull, $"編號為{dto.Id}的文章信息無法找到");
              }
      
              // todo:
              // 在這里要檢查 dto 的合法性,比如文章標(biāo)題要求唯一,dto.Title 就要驗(yàn)證唯一性
              // 在這里要檢查 entity 的合法性,比如文章已鎖定,就不允許編輯,要進(jìn)行攔截
      
              entity = dto.MapTo(entity);
      
              // todo:
              // 在這里要進(jìn)行其他實(shí)體的關(guān)聯(lián)更新,比如添加文章的編輯記錄
      
              count += await PostRepository.UpdateAsync(entity);
          }
      
          if (count > 0)
          {
              return new OperationResult(OperationResultType.Success, $"{dtos.Length}個(gè)文章信息更新成功");
          }
          return OperationResult.NoChanged;
      }
      
      

      批量操作改進(jìn)

      這是個(gè)重復(fù)性很大的繁瑣工作,整個(gè)流程中只有第2步和第4步是變化的,其余步驟都相對固定。為了簡化這類操作,我們可以將第2、4步驟變化點(diǎn)封裝起來,使用 委托 將操作內(nèi)容作為參數(shù)傳進(jìn)來。
      OSharp在 數(shù)據(jù)倉儲IRepository<TEntity, TKey> 中定義了關(guān)于這類 IInputDto 類型參數(shù)的實(shí)體批量操作API。

      例如批量更新,實(shí)現(xiàn)如下:

      /// <summary>
      /// 異步以DTO為載體批量更新實(shí)體
      /// </summary>
      /// <typeparam name="TEditDto">更新DTO類型</typeparam>
      /// <param name="dtos">更新DTO信息集合</param>
      /// <param name="checkAction">更新信息合法性檢查委托</param>
      /// <param name="updateFunc">由DTO到實(shí)體的轉(zhuǎn)換委托</param>
      /// <returns>業(yè)務(wù)操作結(jié)果</returns>
      public virtual async Task<OperationResult> UpdateAsync<TEditDto>(ICollection<TEditDto> dtos,
          Func<TEditDto, TEntity, Task> checkAction = null,
          Func<TEditDto, TEntity, Task<TEntity>> updateFunc = null) where TEditDto : IInputDto<TKey>
      {
          List<string> names = new List<string>();
          foreach (TEditDto dto in dtos)
          {
              try
              {
                  TEntity entity = await _dbSet.FindAsync(dto.Id);
                  if (entity == null)
                  {
                      return new OperationResult(OperationResultType.QueryNull);
                  }
                  if (checkAction != null)
                  {
                      await checkAction(dto, entity);
                  }
                  entity = dto.MapTo(entity);
                  if (updateFunc != null)
                  {
                      entity = await updateFunc(dto, entity);
                  }
                  entity = CheckUpdate(entity)[0];
                  _dbContext.Update<TEntity, TKey>(entity);
              }
              catch (OsharpException e)
              {
                  return new OperationResult(OperationResultType.Error, e.Message);
              }
              catch (Exception e)
              {
                  _logger.LogError(e, e.Message);
                  return new OperationResult(OperationResultType.Error, e.Message);
              }
              names.AddIfNotNull(GetNameValue(dto));
          }
          int count = await _dbContext.SaveChangesAsync(_cancellationTokenProvider.Token);
          return count > 0
              ? new OperationResult(OperationResultType.Success,
                  names.Count > 0
                      ? "信息“{0}”更新成功".FormatWith(names.ExpandAndToString())
                      : "{0}個(gè)信息更新成功".FormatWith(dtos.Count))
              : new OperationResult(OperationResultType.NoChanged);
      }
      

      如上高亮代碼,此方法定義了 Func<TEditDto, TEntity, Task> checkActionFunc<TEditDto, TEntity, Task<TEntity>> updateFunc 兩個(gè)委托參數(shù)作為 更新前參數(shù)檢查更新后關(guān)聯(lián)更新 的操作傳入方式,方法中是以 OsharpException 類型異常來作為中止信號的,如果需要在委托中中止操作,直接拋 OsharpException 異常即可。在調(diào)用時(shí),即可極大簡化批量更新的操作,如上的更新代碼,簡化如下:

      /// <summary>
      /// 更新文章信息
      /// </summary>
      /// <param name="dtos">包含更新信息的文章信息DTO信息</param>
      /// <returns>業(yè)務(wù)操作結(jié)果</returns>
      public virtual async Task<OperationResult> UpdatePosts(params PostInputDto[] dtos)
      {
          Check.Validate<PostInputDto, int>(dtos, nameof(dtos));
      
          return await PostRepository.UpdateAsync(dtos, async (dto, entity) =>
              {
                  // todo:
                  // 在這里要檢查 dto 的合法性,比如文章標(biāo)題要求唯一,dto.Title 就要驗(yàn)證唯一性
                  // 在這里要檢查 entity 的合法性,比如文章已鎖定,就不允許編輯,要進(jìn)行攔截
              },
              async (dto, entity) =>
              {
                  // todo:
                  // 在這里要進(jìn)行其他實(shí)體的關(guān)聯(lián)更新,比如添加文章的編輯記錄
      
                  return entity;
              });
      }
      

      如果沒有必要做額外的 更新前檢查 和更新后的 關(guān)聯(lián)更新,上面的批量更新可以簡化到極致:

      /// <summary>
      /// 更新文章信息
      /// </summary>
      /// <param name="dtos">包含更新信息的文章信息DTO信息</param>
      /// <returns>業(yè)務(wù)操作結(jié)果</returns>
      public virtual async Task<OperationResult> UpdatePosts(params PostInputDto[] dtos)
      {
          Check.Validate<PostInputDto, int>(dtos, nameof(dtos));
      
          return await PostRepository.UpdateAsync(dtos);
      }
      

      服務(wù)層的事務(wù)管理

      事務(wù)開啟與重用

      OSharp的數(shù)據(jù)層在一次業(yè)務(wù)處理請求中遇到數(shù)據(jù)的 新增、更新、刪除 操作并第一次執(zhí)行 SaveChanges 操作時(shí),會自動開啟手動事務(wù),以后再次執(zhí)行 SaveChanges 操作時(shí),會直接使用 同一連接對象 的現(xiàn)有事務(wù),以保證一次業(yè)務(wù)請求的操作都自在一個(gè)事務(wù)內(nèi)。

      public override int SaveChanges()
      {
          // ...
      
          //開啟或使用現(xiàn)有事務(wù)
          BeginOrUseTransaction();
      
          int count = base.SaveChanges();
          
          // ...
      
          return count;
      }
      

      事務(wù)提交

      為了方便事務(wù)管理和不同的服務(wù)層之間的事務(wù)同步,OSharp框架默認(rèn)的事務(wù)提交是在 API 層通過 MVC 的 UnitOfWorkAttribute 特性來提交的。

      /// <summary>
      /// 新用戶注冊
      /// </summary>
      /// <param name="dto">注冊信息</param>
      /// <returns>JSON操作結(jié)果</returns>
      [HttpPost]
      [ServiceFilter(typeof(UnitOfWorkAttribute))]
      [ModuleInfo]
      [Description("用戶注冊")]
      public async Task<AjaxResult> Register(RegisterDto dto)
      {
          // ...
      }
      

      當(dāng)然,你也可以不在 API 層標(biāo)注 [UnitOfWorkAttribute],而是在需要的時(shí)候通過 IUnitOfWork.Commit() 手動提交事務(wù)

      IUnitOfWork unitOfWork = HttpContext.RequestServices.GetUnitOfWork<User, int>();
      unitOfWork.Commit();
      

      業(yè)務(wù)服務(wù)事件訂閱與發(fā)布

      業(yè)務(wù)服務(wù)事件,是通過 事件總線EventBus 來實(shí)現(xiàn)的,OSharp構(gòu)建了一個(gè)簡單的事件總線基礎(chǔ)建設(shè),可以很方便地訂閱和發(fā)布業(yè)務(wù)事件。

      訂閱事件

      訂閱事件很簡單,只需要定義一組配套的 事件數(shù)據(jù)EventData 和相應(yīng)的 事件處理器EventHandler,即可完成事件訂閱的工作。

      IEventData

      事件數(shù)據(jù)EventData 是業(yè)務(wù)服務(wù)發(fā)布事件時(shí)向事件總線傳遞的數(shù)據(jù),每一種業(yè)務(wù),都有特定的事件數(shù)據(jù),一個(gè)事件數(shù)據(jù)可觸發(fā)多個(gè)事件處理器
      定義一個(gè)事件數(shù)據(jù),需要實(shí)現(xiàn) IEventData 接口

      /// <summary>
      /// 定義事件數(shù)據(jù),所有事件都要實(shí)現(xiàn)該接口
      /// </summary>
      public interface IEventData
      {
          /// <summary>
          /// 獲取 事件編號
          /// </summary>
          Guid Id { get; }
      
          /// <summary>
          /// 獲取 事件發(fā)生的時(shí)間
          /// </summary>
          DateTime EventTime { get; }
      
          /// <summary>
          /// 獲取或設(shè)置 事件源,觸發(fā)事件的對象
          /// </summary>
          object EventSource { get; set; }
      }
      

      EventDataBase

      為了方便 事件數(shù)據(jù) 的定義,OSharp定義了一個(gè)通用事件數(shù)據(jù)基類EventDataBase,繼承此基類,只需要添加事件觸發(fā)需要的業(yè)務(wù)數(shù)據(jù)即可

      /// <summary>
      /// 事件源數(shù)據(jù)信息基類
      /// </summary>
      public abstract class EventDataBase : IEventData
      {
          /// <summary>
          /// 初始化一個(gè)<see cref="EventDataBase"/>類型的新實(shí)例
          /// </summary>
          protected EventDataBase()
          {
              Id = Guid.NewGuid();
              EventTime = DateTime.Now;
          }
      
          /// <summary>
          /// 獲取 事件編號
          /// </summary>
          public Guid Id { get; }
      
          /// <summary>
          /// 獲取 事件發(fā)生時(shí)間
          /// </summary>
          public DateTime EventTime { get; }
      
          /// <summary>
          /// 獲取或設(shè)置 觸發(fā)事件的對象
          /// </summary>
          public object EventSource { get; set; }
      }
      

      IEventHandler

      業(yè)務(wù)事件的處理邏輯,是通過 事件處理器 EventHandler 來實(shí)現(xiàn)的,事件處理器應(yīng)遵從 單一職責(zé) 原則,一個(gè)處理器只做一件事,業(yè)務(wù)服務(wù)層發(fā)布一項(xiàng) 事件數(shù)據(jù),可觸發(fā)多個(gè) 事件處理器

      /// <summary>
      /// 定義事件處理器,所有事件處理都要實(shí)現(xiàn)該接口
      /// EventBus中,Handler的調(diào)用是同步執(zhí)行的,如果需要觸發(fā)就不管的異步執(zhí)行,可以在實(shí)現(xiàn)EventHandler的Handle邏輯時(shí)使用Task.Run
      /// </summary>
      [IgnoreDependency]
      public interface IEventHandler
      {
          /// <summary>
          /// 是否可處理指定事件
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          /// <returns>是否可處理</returns>
          bool CanHandle(IEventData eventData);
      
          /// <summary>
          /// 事件處理
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          void Handle(IEventData eventData);
      
          /// <summary>
          /// 異步事件處理
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          /// <param name="cancelToken">異步取消標(biāo)識</param>
          /// <returns></returns>
          Task HandleAsync(IEventData eventData, CancellationToken cancelToken = default(CancellationToken));
      }
      

      泛型事件處理器

      /// <summary>
      /// 定義泛型事件處理器
      /// EventBus中,Handler的調(diào)用是同步執(zhí)行的,如果需要觸發(fā)就不管的異步執(zhí)行,可以在實(shí)現(xiàn)EventHandler的Handle邏輯時(shí)使用Task.Run
      /// </summary>
      /// <typeparam name="TEventData">事件源數(shù)據(jù)</typeparam>
      [IgnoreDependency]
      public interface IEventHandler<in TEventData> : IEventHandler where TEventData : IEventData
      {
          /// <summary>
          /// 事件處理
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          void Handle(TEventData eventData);
      
          /// <summary>
          /// 異步事件處理
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          /// <param name="cancelToken">異步取消標(biāo)識</param>
          Task HandleAsync(TEventData eventData, CancellationToken cancelToken = default(CancellationToken));
      }
      

      EventHandlerBase

      同樣的,為了方便 事件處理器 的定義,OSharp定義了一個(gè)通用的事件處理器基類EventHandlerBase<TEventData>,繼承此基類,只需要實(shí)現(xiàn)核心的事件處理邏輯即可

      /// <summary>
      /// 事件處理器基類
      /// </summary>
      public abstract class EventHandlerBase<TEventData> : IEventHandler<TEventData> where TEventData : IEventData
      {
          /// <summary>
          /// 是否可處理指定事件
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          /// <returns>是否可處理</returns>
          public virtual bool CanHandle(IEventData eventData)
          {
              return eventData.GetType() == typeof(TEventData);
          }
      
          /// <summary>
          /// 事件處理
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          public virtual void Handle(IEventData eventData)
          {
              if (!CanHandle(eventData))
              {
                  return;
              }
              Handle((TEventData)eventData);
          }
      
          /// <summary>
          /// 異步事件處理
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          /// <param name="cancelToken">異步取消標(biāo)識</param>
          /// <returns></returns>
          public virtual Task HandleAsync(IEventData eventData, CancellationToken cancelToken = default(CancellationToken))
          {
              if (!CanHandle(eventData))
              {
                  return Task.FromResult(0);
              }
              return HandleAsync((TEventData)eventData, cancelToken);
          }
      
          /// <summary>
          /// 事件處理
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          public abstract void Handle(TEventData eventData);
      
          /// <summary>
          /// 異步事件處理
          /// </summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          /// <param name="cancelToken">異步取消標(biāo)識</param>
          /// <returns>是否成功</returns>
          public virtual Task HandleAsync(TEventData eventData, CancellationToken cancelToken = default(CancellationToken))
          {
              return Task.Run(() => Handle(eventData), cancelToken);
          }
      }
      

      發(fā)布事件

      事件的發(fā)布,就相當(dāng)簡單了,只需要實(shí)例化一個(gè)事件數(shù)據(jù)EventData的實(shí)例,然后通過IEventBus.Publish(eventData)即可發(fā)布事件,觸發(fā)該EventData的所有訂閱處理器

      XXXEventData eventData = new XXXEventData()
      {
          // ...
      };
      EventBus.Publish(eventData);
      
      

      博客模塊的業(yè)務(wù)事件實(shí)現(xiàn)

      回到我們的 Liuliu.Blogs 項(xiàng)目,根據(jù) <業(yè)務(wù)模塊設(shè)計(jì)#博客業(yè)務(wù)需求分析> 的需求分析的第二條,審核博客之后需要發(fā)郵件通知用戶,發(fā)郵件屬于審核博客業(yè)務(wù)計(jì)劃外的需求,使用 業(yè)務(wù)事件 來實(shí)現(xiàn)正當(dāng)其時(shí)。

      • 審核博客業(yè)務(wù)事件數(shù)據(jù)
      /// <summary>
      /// 審核博客事件數(shù)據(jù)
      /// </summary>
      public class VerifyBlogEventData : EventDataBase
      {
          /// <summary>
          /// 獲取或設(shè)置 博客名稱
          /// </summary>
          public string BlogName { get; set; }
      
          /// <summary>
          /// 獲取或設(shè)置 用戶名
          /// </summary>
          public string UserName { get; set; }
      
          /// <summary>
          /// 獲取或設(shè)置 審核是否通過
          /// </summary>
          public bool IsEnabled { get; set; }
      }
      
      • 審核博客業(yè)務(wù)事件處理器
      /// <summary>
      /// 審核博客事件處理器
      /// </summary>
      public class VerifyBlogEventHandler : EventHandlerBase<VerifyBlogEventData>
      {
          private readonly ILogger _logger;
      
          /// <summary>
          /// 初始化一個(gè)<see cref="VerifyBlogEventHandler"/>類型的新實(shí)例
          /// </summary>
          public VerifyBlogEventHandler(IServiceProvider serviceProvider)
          {
              _logger = serviceProvider.GetService<ILoggerFactory>().CreateLogger<VerifyBlogEventHandler>();
          }
      
          /// <summary>事件處理</summary>
          /// <param name="eventData">事件源數(shù)據(jù)</param>
          public override void Handle(VerifyBlogEventData eventData)
          {
              _logger.LogInformation(
                  $"觸發(fā) 審核博客事件處理器,用戶“{eventData.UserName}”的博客“{eventData.BlogName}”審核結(jié)果:{(eventData.IsEnabled ? "通過" : "未通過")}");
          }
      }
      

      博客模塊的服務(wù)實(shí)現(xiàn)

      回到我們的 Liuliu.Blogs 項(xiàng)目,根據(jù) <業(yè)務(wù)模塊設(shè)計(jì)#服務(wù)層> 的需求分析,綜合使用OSharp框架提供的基礎(chǔ)建設(shè),博客模塊的業(yè)務(wù)服務(wù)實(shí)現(xiàn)如下:

      BlogsService.cs

      /// <summary>
      /// 業(yè)務(wù)服務(wù)實(shí)現(xiàn):博客模塊
      /// </summary>
      public partial class BlogsService : IBlogsContract
      {
          private readonly IServiceProvider _serviceProvider;
      
          /// <summary>
          /// 初始化一個(gè)<see cref="BlogsService"/>類型的新實(shí)例
          /// </summary>
          public BlogsService(IServiceProvider serviceProvider)
          {
              _serviceProvider = serviceProvider;
          }
      
          /// <summary>
          /// 獲取 博客倉儲對象
          /// </summary>
          protected IRepository<Blog, int> BlogRepository => _serviceProvider.GetService<IRepository<Blog, int>>();
      
          /// <summary>
          /// 獲取 文章倉儲對象
          /// </summary>
          protected IRepository<Post, int> PostRepository => _serviceProvider.GetService<IRepository<Post, int>>();
      
          /// <summary>
          /// 獲取 用戶倉儲對象
          /// </summary>
          protected IRepository<User, int> UserRepository => _serviceProvider.GetService<IRepository<User, int>>();
      }
      

      BlogsService.Blog.cs

      public partial class BlogsService
      {
          /// <summary>
          /// 獲取 博客信息查詢數(shù)據(jù)集
          /// </summary>
          public virtual IQueryable<Blog> Blogs => BlogRepository.Query();
      
          /// <summary>
          /// 檢查博客信息是否存在
          /// </summary>
          /// <param name="predicate">檢查謂語表達(dá)式</param>
          /// <param name="id">更新的博客信息編號</param>
          /// <returns>博客信息是否存在</returns>
          public virtual Task<bool> CheckBlogExists(Expression<Func<Blog, bool>> predicate, int id = 0)
          {
              Check.NotNull(predicate, nameof(predicate));
              return BlogRepository.CheckExistsAsync(predicate, id);
          }
      
          /// <summary>
          /// 申請博客信息
          /// </summary>
          /// <param name="dto">申請博客信息DTO信息</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          public virtual async Task<OperationResult> ApplyForBlog(BlogInputDto dto)
          {
              Check.Validate(dto, nameof(dto));
      
              // 博客是以當(dāng)前用戶的身份來申請的
              ClaimsPrincipal principal = _serviceProvider.GetCurrentUser();
              if (principal == null || !principal.Identity.IsAuthenticated)
              {
                  return new OperationResult(OperationResultType.Error, "用戶未登錄或登錄已失效");
              }
      
              int userId = principal.Identity.GetUserId<int>();
              User user = await UserRepository.GetAsync(userId);
              if (user == null)
              {
                  return new OperationResult(OperationResultType.QueryNull, $"編號為“{userId}”的用戶信息不存在");
              }
              Blog blog = BlogRepository.TrackQuery(m => m.UserId == userId).FirstOrDefault();
              if (blog != null)
              {
                  return new OperationResult(OperationResultType.Error, "當(dāng)前用戶已開通博客,不能重復(fù)申請");
              }
      
              if (await CheckBlogExists(m => m.Url == dto.Url))
              {
                  return new OperationResult(OperationResultType.Error, $"Url 為“{dto.Url}”的博客已存在,不能重復(fù)添加");
              }
              blog = dto.MapTo<Blog>();
              blog.UserId = userId;
              int count = await BlogRepository.InsertAsync(blog);
              return count > 0
                  ? new OperationResult(OperationResultType.Success, "博客申請成功")
                  : OperationResult.NoChanged;
          }
      
          /// <summary>
          /// 審核博客信息
          /// </summary>
          /// <param name="id">博客編號</param>
          /// <param name="isEnabled">是否通過</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          public virtual async Task<OperationResult> VerifyBlog(int id, bool isEnabled)
          {
              Blog blog = await BlogRepository.GetAsync(id);
              if (blog == null)
              {
                  return new OperationResult(OperationResultType.QueryNull, $"編號為“{id}”的博客信息不存在");
              }
      
              // 更新博客
              blog.IsEnabled = isEnabled;
              int count = await BlogRepository.UpdateAsync(blog);
      
              User user = await UserRepository.GetAsync(blog.UserId);
              if (user == null)
              {
                  return new OperationResult(OperationResultType.QueryNull, $"編號為“{blog.UserId}”的用戶信息不存在");
              }
      
              // 如果開通博客,給用戶開通博主身份
              if (isEnabled)
              {
                  // 查找博客主的角色,博主角色名可由配置系統(tǒng)獲得
                  const string roleName = "博主";
                  // 用于CUD操作的實(shí)體,要用 TrackQuery 方法來查詢出需要的數(shù)據(jù),不能用 Query,因?yàn)?Query 會使用 AsNoTracking
                  Role role = RoleRepository.TrackQuery(m => m.Name == roleName).FirstOrDefault();
                  if (role == null)
                  {
                      return new OperationResult(OperationResultType.QueryNull, $"名稱為“{roleName}”的角色信息不存在");
                  }
      
                  UserRole userRole = UserRoleRepository.TrackQuery(m => m.UserId == user.Id && m.RoleId == role.Id)
                      .FirstOrDefault();
                  if (userRole == null)
                  {
                      userRole = new UserRole() { UserId = user.Id, RoleId = role.Id, IsLocked = false };
                      count += await UserRoleRepository.InsertAsync(userRole);
                  }
              }
      
              OperationResult result = count > 0
                  ? new OperationResult(OperationResultType.Success, $"博客“{blog.Display}”審核 {(isEnabled ? "通過" : "未通過")}")
                  : OperationResult.NoChanged;
              if (result.Succeeded)
              {
                  VerifyBlogEventData eventData = new VerifyBlogEventData()
                  {
                      BlogName = blog.Display,
                      UserName = user.NickName,
                      IsEnabled = isEnabled
                  };
                  EventBus.Publish(eventData);
              }
      
              return result;
          }
      
          /// <summary>
          /// 更新博客信息
          /// </summary>
          /// <param name="dtos">包含更新信息的博客信息DTO信息</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          public virtual Task<OperationResult> UpdateBlogs(params BlogInputDto[] dtos)
          {
              return BlogRepository.UpdateAsync(dtos, async (dto, entity) =>
              {
                  if (await BlogRepository.CheckExistsAsync(m => m.Url == dto.Url, dto.Id))
                  {
                      throw new OsharpException($"Url為“{dto.Url}”的博客已存在,不能重復(fù)");
                  }
              });
          }
      
          /// <summary>
          /// 刪除博客信息
          /// </summary>
          /// <param name="ids">要?jiǎng)h除的博客信息編號</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          public virtual Task<OperationResult> DeleteBlogs(params int[] ids)
          {
              return BlogRepository.DeleteAsync(ids, entity =>
              {
                  if (PostRepository.Query(m => m.BlogId == entity.Id).Any())
                  {
                      throw new OsharpException($"博客“{entity.Display}”中還有文章未刪除,請先刪除所有文章,再刪除博客");
                  }
                  return Task.FromResult(0);
              });
          }
      }
      

      BlogsService.Post.cs

      public partial class BlogsService
      {
          /// <summary>
          /// 獲取 文章信息查詢數(shù)據(jù)集
          /// </summary>
          public virtual IQueryable<Post> Posts => PostRepository.Query();
      
          /// <summary>
          /// 檢查文章信息是否存在
          /// </summary>
          /// <param name="predicate">檢查謂語表達(dá)式</param>
          /// <param name="id">更新的文章信息編號</param>
          /// <returns>文章信息是否存在</returns>
          public virtual Task<bool> CheckPostExists(Expression<Func<Post, bool>> predicate, int id = 0)
          {
              Check.NotNull(predicate, nameof(predicate));
              return PostRepository.CheckExistsAsync(predicate, id);
          }
      
          /// <summary>
          /// 添加文章信息
          /// </summary>
          /// <param name="dtos">要添加的文章信息DTO信息</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          public virtual async Task<OperationResult> CreatePosts(params PostInputDto[] dtos)
          {
              Check.Validate<PostInputDto, int>(dtos, nameof(dtos));
              if (dtos.Length == 0)
              {
                  return OperationResult.NoChanged;
              }
      
              // 文章是以當(dāng)前用戶身份來添加的
              ClaimsPrincipal principal = _serviceProvider.GetCurrentUser();
              if (principal == null || !principal.Identity.IsAuthenticated)
              {
                  throw new OsharpException("用戶未登錄或登錄已失效");
              }
              // 檢查當(dāng)前用戶的博客狀態(tài)
              int userId = principal.Identity.GetUserId<int>();
              Blog blog = BlogRepository.TrackQuery(m => m.UserId == userId).FirstOrDefault();
              if (blog == null || !blog.IsEnabled)
              {
                  throw new OsharpException("當(dāng)前用戶的博客未開通,無法添加文章");
              }
      
              // 沒有前置檢查,checkAction為null
              return await PostRepository.InsertAsync(dtos, null, (dto, entity) =>
              {
                  // 給新建的文章關(guān)聯(lián)博客和作者
                  entity.BlogId = blog.Id;
                  entity.UserId = userId;
                  return Task.FromResult(entity);
              });
          }
      
          /// <summary>
          /// 更新文章信息
          /// </summary>
          /// <param name="dtos">包含更新信息的文章信息DTO信息</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          public virtual Task<OperationResult> UpdatePosts(params PostInputDto[] dtos)
          {
              Check.Validate<PostInputDto, int>(dtos, nameof(dtos));
      
              return PostRepository.UpdateAsync(dtos);
          }
      
          /// <summary>
          /// 刪除文章信息
          /// </summary>
          /// <param name="ids">要?jiǎng)h除的文章信息編號</param>
          /// <returns>業(yè)務(wù)操作結(jié)果</returns>
          public virtual Task<OperationResult> DeletePosts(params int[] ids)
          {
              Check.NotNull(ids, nameof(ids));
              return PostRepository.DeleteAsync(ids);
          }
      }
      
      

      模塊入口 BlogsPack

      模塊入口基類

      非AspNetCore模塊基類 OsharpPack

      前面多次提到,每個(gè)Pack模塊都是繼承自一個(gè) 模塊基類OsharpPack,這個(gè)基類用于定義 模塊初始化UsePack 過程中未涉及 AspNetCore 環(huán)境的模塊。

      /// <summary>
      /// OSharp模塊基類
      /// </summary>
      public abstract class OsharpPack
      {
          /// <summary>
          /// 獲取 模塊級別,級別越小越先啟動
          /// </summary>
          public virtual PackLevel Level => PackLevel.Business;
      
          /// <summary>
          /// 獲取 模塊啟動順序,模塊啟動的順序先按級別啟動,同一級別內(nèi)部再按此順序啟動,
          /// 級別默認(rèn)為0,表示無依賴,需要在同級別有依賴順序的時(shí)候,再重寫為>0的順序值
          /// </summary>
          public virtual int Order => 0;
      
          /// <summary>
          /// 獲取 是否已可用
          /// </summary>
          public bool IsEnabled { get; protected set; }
      
          /// <summary>
          /// 將模塊服務(wù)添加到依賴注入服務(wù)容器中
          /// </summary>
          /// <param name="services">依賴注入服務(wù)容器</param>
          /// <returns></returns>
          public virtual IServiceCollection AddServices(IServiceCollection services)
          {
              return services;
          }
      
          /// <summary>
          /// 應(yīng)用模塊服務(wù)
          /// </summary>
          /// <param name="provider">服務(wù)提供者</param>
          public virtual void UsePack(IServiceProvider provider)
          {
              IsEnabled = true;
          }
      
          /// <summary>
          /// 獲取當(dāng)前模塊的依賴模塊類型
          /// </summary>
          /// <returns></returns>
          internal Type[] GetDependPackTypes(Type packType = null)
          {
              // ...
          }
      }
      

      模塊基類OsharpPack 定義了兩個(gè)可重寫屬性:

      • PackLevel:模塊級別,級別越小越先啟動
        模塊級別按 模塊 在框架中不同的功能層次,定義了如下幾個(gè)級別:
      /// <summary>
      /// 模塊級別,級別越核心,優(yōu)先啟動
      /// </summary>
      public enum PackLevel
      {
          /// <summary>
          /// 核心級別,表示系統(tǒng)的核心模塊,
          /// 這些模塊不涉及第三方組件,在系統(tǒng)運(yùn)行中是不可替換的,核心模塊將始終加載
          /// </summary>
          Core = 1,
          /// <summary>
          /// 框架級別,表示涉及第三方組件的基礎(chǔ)模塊
          /// </summary>
          Framework = 10,
          /// <summary>
          /// 應(yīng)用級別,表示涉及應(yīng)用數(shù)據(jù)的基礎(chǔ)模塊
          /// </summary>
          Application = 20,
          /// <summary>
          /// 業(yè)務(wù)級別,表示涉及真實(shí)業(yè)務(wù)處理的模塊
          /// </summary>
          Business = 30
      }
      
      • Order:級別內(nèi)模塊啟動順序,模塊啟動的順序先按級別啟動,同一級別內(nèi)部再按此順序啟動,級別默認(rèn)為 0,表示無依賴,需要在同級別有依賴順序的時(shí)候,再重寫為 >0 的順序值

      同時(shí),模塊基類 還定義了兩個(gè)方法:

      • AddServices:用于將模塊內(nèi)定義的服務(wù)注入到 依賴注入服務(wù)容器 中。
      • UsePack:用于使用服務(wù)對當(dāng)前模塊進(jìn)行初始化。

      AspNetCore模塊基類 AspOsharpPack

      AspOsharpPack 基類繼承了 OsharpPack,添加了一個(gè)對 IApplicationBuilder 支持的 UsePack 方法,用于實(shí)現(xiàn)與 AspNetCore 關(guān)聯(lián)的模塊初始化工作,例如 Mvc模塊 初始化的時(shí)候需要應(yīng)用中間件:app.UseMvcWithAreaRoute();

      /// <summary>
      ///  基于AspNetCore環(huán)境的Pack模塊基類
      /// </summary>
      public abstract class AspOsharpPack : OsharpPack
      {
          /// <summary>
          /// 應(yīng)用AspNetCore的服務(wù)業(yè)務(wù)
          /// </summary>
          /// <param name="app">Asp應(yīng)用程序構(gòu)建器</param>
          public virtual void UsePack(IApplicationBuilder app)
          {
              base.UsePack(app.ApplicationServices);
          }
      }
      

      博客模塊的模塊入口實(shí)現(xiàn)

      回到我們的 Liuliu.Blogs 項(xiàng)目,我們來實(shí)現(xiàn)投票模塊的模塊入口類 BlogsPack

      • 博客模塊屬于業(yè)務(wù)模塊,因此 PackLevel 設(shè)置為 Business
      • 博客模塊的啟動順序無需重寫,保持 0 即可
      • 將 博客業(yè)務(wù)服務(wù) 注冊到 服務(wù)容器中
      • 無甚初始化業(yè)務(wù)

      實(shí)現(xiàn)代碼如下:

      /// <summary>
      /// 博客模塊
      /// </summary>
      public class BlogsPack : OsharpPack
      {
          /// <summary>
          /// 獲取 模塊級別,級別越小越先啟動
          /// </summary>
          public override PackLevel Level { get; } = PackLevel.Business;
      
          /// <summary>將模塊服務(wù)添加到依賴注入服務(wù)容器中</summary>
          /// <param name="services">依賴注入服務(wù)容器</param>
          /// <returns></returns>
          public override IServiceCollection AddServices(IServiceCollection services)
          {
              services.TryAddScoped<IBlogsContract, BlogsService>();
      
              return services;
          }
      }
      

      至此,博客模塊的服務(wù)層實(shí)現(xiàn)完畢。

      posted @ 2019-05-11 08:30  郭明鋒  閱讀(4342)  評論(16)    收藏  舉報(bào)

      主站蜘蛛池模板: 色欲av亚洲一区无码少妇| 日韩精品一区二区三区四| 国产精品亚洲片在线观看麻豆| 欧美、另类亚洲日本一区二区| 亚洲欧美日韩人成在线播放| 国产成人亚洲老熟女精品| 国产免费福利网站| 欧洲中文字幕一区二区| 成年女性特黄午夜视频免费看| 在线中文字幕国产一区| 无码国内精品人妻少妇| 精品无码久久久久久久久久| av无码小缝喷白浆在线观看| 正在播放的国产A一片| 国产精品爽爽v在线观看无码| 亚洲欧美自偷自拍视频图片| 国产福利片一区二区三区| 中文字幕人妻中文AV不卡专区| 伊人久久大香线蕉AV网禁呦| 亚洲人成色99999在线观看| 亚洲国产高清精品线久久| 长岭县| 日韩精品一区二区三区vr| 亚洲日本精品一区二区| 中文在线最新版天堂| 国产精品一区二区久久精品| 日韩精品亚洲不卡一区二区| 亚洲精品日本久久一区二区三区| 亚洲精品日韩在线丰满| 蜜桃av无码免费看永久| 日韩有码中文字幕av| 亚洲av日韩在线资源| A级毛片100部免费看| 无码人妻斩一区二区三区| 国产网红女主播精品视频 | 91热在线精品国产一区| 亚洲AV高清一区二区三区尤物| 亚洲色最新高清AV网站| 亚洲老妇女亚洲老熟女久| 国产亚洲视频免费播放| 国产精品免费视频不卡|