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

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

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

      新思想、新技術、新架構——更好更快的開發現代ASP.NET應用程序(續1)

      今天在@張善友@田園里的蟋蟀的博客看到微軟“.Net社區虛擬大會”dotnetConf2015的信息,感謝他們的真誠付出!真希望自已也能為中國的.NET社區貢獻綿薄之力。


        上周星期天開通了博客并發布了第一篇文章《新思想、新技術、新架構——更好更快的開發現代ASP.NET應用程序》,匯集了一些比較流行的技術和開源項目,也把自己的程序架構、部分代碼風格、前端表現簡單做了一些展示,引起了近100位朋友的評論。特別感謝@田園里的蟋蟀、@深藍醫生、@郭明鋒、@瘋狂的提子、@jimcsharp、@以吾之名等給我建議和指導的朋友,也感謝那些給我支持和鼓勵的朋友。還有對我提出批評的朋友,說我的面試題的內容不當,也很感謝他們讓我更注意言辭,但并不會影響我對面試者基礎知識的重視程度。


        上周發布那篇文章主要是因為這段時間在招聘過程中發現幾乎所有面試者對基礎知識和新技術都知之甚少,有過幾年工作經驗的程序員也幾乎只會單一模式的CURD,沒有明顯的技術特長,所以我想分享一些自己認為比較好的思想、技術、架構模式,引起更多ASP.NET程序員的思考和討論。


        其實,上周星期天是花了大半天寫一篇博客,在發出來之前刪掉了一大半內容(一些講述我自己心路歷程的內容),因為我在博客園是一個新人,在沒有對別人提供價值幫助之前也許沒人關心我是誰。那天由于時間太晚了,很多想寫的內容都沒有寫出來,發布的時候僅貼了一些圖片,后來在評論中寫了很多內容,并修改了原文正文,補充分享了一些非常好的開源項目。希望之前看過的朋友可以再回去看看,給個鏈接:http://www.rzrgm.cn/mienreal/p/4340864.html


        之前的一個項目是做的微信公眾平臺的第三方平臺,提供微網站自主建站、會員卡、微商城、外賣預訂等幾十項功能。在項目初期,我僅擔任產品總監負責產品設計,后來因為沒有強大的前端團隊,不得不親自實現微官網的可視化設計器的前端。再后來公司讓我接管了開發部(全是JAVA開發人員),跟開發團隊有了更直接的配合。我發現他們普遍代碼質量不高,幾乎不懂得運用設計模式和最佳實踐。每新增或修改一點功能,都要將全部代碼進行編譯和發布,會影響正在登錄使用的用戶,而且有時候一個經驗不足的程序員修改的一點東西會讓整個平臺不能正常啟動。跟幾個高級工程師多次溝通,希望他們學習新技術新思想,運用成熟的最佳實踐來提高代碼質量;希望他們了解領域驅動設計用于會員卡等業務較復雜的模塊;希望他們能了解OSGI實現模塊化開發和部署,但因為經驗能力和積極性等原因,這些愿望都沒有實現。后來在新項目(開發代號Fami)中,我選擇了.NET技術平臺,并組建新的開發團隊來進行這個項目?,F在項目才剛完成基礎框架和項目規范。


        下面把這個項目的架構思想和功能特性再分享一下。希望對正在設計架構的朋友有一個參考作用。本項目是Saas模式的在線產品,需實現多租戶模式;有多個功能模塊,且上線時間有先有后,需實現模塊化開發。

       

      本項目總體分為兩個部分:一個基礎框架組件,一個Fami解決方案。

      基礎框架組件的功能:
      1、基礎框架組件獨立、通用,可用于多個不同項目。類似于daxnet的Apworks框架。
      2、對項目實現模塊化開發提供了支持,每個模塊有獨立的EF DbContext,可單獨指定數據庫。
      3、對DDD的技術實現進行了封裝,讓項目以極精簡的代碼,專注于業務領域。
      4、多租戶支持,每個租戶的數據自動隔離,業務模塊開發者不需要手動操作TenantId。
      5、集成ASP.NET Identity,實現登錄認證、功能權限授權&驗證、角色和用戶管理。
      6、集成Log4Net,實現日志記錄。
      7、集成AutoMapper,實現Dto類與實體類的雙向自動轉換。
      8、實現UnitOfWork模式,為應用層和倉儲層的(會寫數據庫的)方法自動實現數據庫事務。
      9、可通過ApplicationService的方法自動建立相應的WebApi方法,ajax可直接調用,不需要寫ApiController和Action。
      10、調用ApplicationService的方法時,自動驗證權限和參數有效性(用相應的Attribute標注)。
      11、繼承自FullAuditedEntity基類的領域實體,會自動實現軟刪除(在數據庫中用IsDeleted字段進行標注)。
      12、實現一系列擴展方法,簡化編碼。

       

       

       

      Fami項目解決方案結構圖:

       
      模塊化結構圖   WEB項目結構圖

       

      每個模塊是一個獨立的類庫項目,有獨立的DbContext(如上面左圖中的WechatMpDbContext.cs),可單獨指定不同的數據庫鏈接,以實現按功能模塊分庫。

      每個模塊有自己權限提供類(WechatMpAuthorizationProvider.cs)、設置提供類(WechatMpSettingProvider.cs)、倉儲基類(WechatMpRepository.cs)。

      模塊的展現層代碼(MVC文件)放在WEB項目的Areas下,有自己單獨的路由注冊類文件(如上面右圖中的WechatMpAreaRegistration.cs)。

       

      MVC的Controller只有極少的代碼,用于返回列表頁的View、表單頁面的View和Model,新建、編輯、刪除等操作無需寫Action方法,直接由前端的ajax調用Application層的相應Service方法(運行時,動態代理自動生成ApiController及相應方法)。

      拿一個最最簡單的圖文素材功能舉例說明:

       

      Domain層的Article實體類:

       1 namespace Fami.WechatMp
       2 {
       3     public class Article : AuditedEntityAndTenant
       4     {
       5         [MaxLength(50)]
       6         public string Title { get; set; }
       7 
       8         [MaxLength(512)]
       9         public string PicUrl { get; set; }
      10 
      11         [MaxLength(1000)]
      12         public string Interoduction { get; set; }
      13 
      14         [MaxLength(512)]
      15         public string LinkUrl { get; set; }
      16 
      17         [MaxLength(512)]
      18         public string OriginalUrl { get; set; }
      19 
      20         public string Content { get; set; }
      21 
      22         [ForeignKey("ArticleCategoryId")]
      23         public ArticleCategory ArticleCategory { get; set; }
      24 
      25         public Guid ArticleCategoryId { get; set; }
      26     }
      27 }

       

      Application層的ArticleDto類(用于WEB前端表單與Application層之間傳值):

       1 namespace Fami.WechatMp
       2 {
       3     [AutoMap(typeof(Article))]
       4     public class ArticleDto : EntityDto, IValidate
       5     {
       6         [Required]
       7         [MaxLength(50)]
       8         public string Title { get; set; }
       9 
      10         [MaxLength(512)]
      11         public string PicUrl { get; set; }
      12 
      13         [MaxLength(1000)]
      14         public string Interoduction { get; set; }
      15 
      16         [MaxLength(512)]
      17         public string LinkUrl { get; set; }
      18 
      19         [MaxLength(512)]
      20         public string OriginalUrl { get; set; }
      21 
      22         public string Content { get; set; }
      23 
      24         public Guid ArticleCategoryId { get; set; }
      25     }
      26 }

       

      Application層的ArticleItem類(用于WEB前端查詢列表的顯示):

       1 namespace Fami.WechatMp
       2 {
       3     [AutoMapFrom(typeof(Article))]
       4     public class ArticleItem : EntityDto
       5     {
       6         public string Title { get; set; }
       7 
       8         public string PicUrl { get; set; }
       9 
      10         public string LinkUrl { get; set; }
      11 
      12         public string OriginalUrl { get; set; }
      13 
      14         public string ArticleCategoryCategoryName { get; set; } //會自動讀取ArticleCategory的CategoryName屬性
      15 
      16         public DateTime CreationTime { get; set; }
      17     }
      18 }

       

      Application層的IArticleAppService接口:

       1 namespace Fami.WechatMp
       2 {
       3     public interface IArticleAppService : IApplicationService
       4     {
       5         /// <summary>
       6         /// 獲取素材分類列表(下拉框)
       7         /// </summary>
       8         /// <returns></returns>
       9         Task<IEnumerable<ArticleCategoryDto>> GetArticleCategories();
      10 
      11         #region 素材查詢和更新操作
      12         /// <summary>
      13         /// 創建素材信息
      14         /// </summary>
      15         /// <param name="model"></param>
      16         /// <returns></returns>
      17         Task<ArticleDto> CreateArticle(ArticleDto model);
      18 
      19         /// <summary>
      20         /// 更新素材信息
      21         /// </summary>
      22         /// <param name="model"></param>
      23         /// <returns></returns>
      24         Task UpdateArticle(ArticleDto model);
      25 
      26         /// <summary>
      27         /// 批量刪除素材信息
      28         /// </summary>
      29         /// <param name="input"></param>
      30         /// <returns></returns>
      31         Task BatchDeleteArticle(IEnumerable<Guid> idList);
      32 
      33         /// <summary>
      34         /// 獲取指定的素材信息
      35         /// </summary>
      36         /// <param name="id"></param>
      37         /// <returns></returns>
      38         Task<ArticleDto> GetArticle(Guid id);
      39 
      40         /// <summary>
      41         /// 查詢素材列表信息(Table)
      42         /// </summary>
      43         /// <param name="input"></param>
      44         /// <returns></returns>
      45         Task<QueryResultOutput<ArticleItem>> GetArticleList(GetArticleListInput input);
      46 
      47         #endregion
      48     }
      49 }

       

      Application層的ArticleAppService實現類:

       1 namespace Fami.WechatMp
       2 {
       3     public class ArticleAppService : FamiAppServiceBase, IArticleAppService
       4     {
       5         private readonly IWechatMpRepository<ArticleCategory> _articleCategoryRepository;
       6         private readonly IWechatMpRepository<Article> _articleRepository;
       7         private readonly IArticlePolicy _articlePolicy;
       8 
       9         public ArticleAppService(
      10             IWechatMpRepository<ArticleCategory> articleCategoryRepository,
      11             IWechatMpRepository<Article> articleRepository,
      12             IArticlePolicy articlePolicy
      13             )
      14         {
      15             _articleCategoryRepository = articleCategoryRepository;
      16             _articleRepository = articleRepository;
      17             _articlePolicy = articlePolicy;
      18         }
      19 
      20         public async Task<IEnumerable<ArticleCategoryDto>> GetArticleCategories()
      21         {
      22             var query = _articleCategoryRepository.GetAll().OrderBy(item => item.DisplayOrder);
      23             return await query.Query().To<ArticleCategoryDto>().Take(100).ToListAsync();
      24         }
      25 
      26         public async Task<ArticleDto> CreateArticle(ArticleDto model)
      27         {
      28             if (await _articlePolicy.IsExistsArticleByName(model.Title))
      29             {
      30                 throw new UserFriendlyException(L("NameIsExists"));
      31             }
      32             var entity = await _articleRepository.InsertAsync(model.MapTo<Article>());
      33             return entity.MapTo<ArticleDto>();
      34         }
      35 
      36         public async Task UpdateArticle(ArticleDto model)
      37         {
      38             if (await _articlePolicy.IsExistsArticleByName(model.Title, model.Id))
      39             {
      40                 throw new UserFriendlyException(L("NameIsExists"));
      41             }
      42             var entity = await _articleRepository.GetAsync(model.Id);
      43             await _articleRepository.UpdateAsync(model.MapTo(entity));
      44         }
      45 
      46         public async Task BatchDeleteArticle(IEnumerable<Guid> idList)
      47         {
      48             if (await _articlePolicy.IsExistsByArticleAutoreplySetting(idList.ToList()))
      49             {
      50                 throw new UserFriendlyException(L("AutoreplyArticleIsExists"));
      51             }
      52             await _articleRepository.BatchDeleteAsync(idList);
      53         }
      54 
      55         public async Task<ArticleDto> GetArticle(Guid id)
      56         {
      57             var entity = await _articleRepository.GetAsync(id);
      58             return entity.MapTo<ArticleDto>();
      59         }
      60 
      61         /// <summary>
      62         /// 根據查詢條件,返回文章列表數據
      63         /// </summary>
      64         /// <param name="input">查詢條件</param>
      65         /// <returns></returns>
      66         public async Task<QueryResultOutput<ArticleItem>> GetArticleList(GetArticleListInput input)
      67         {
      68             var query = _articleRepository.GetAll()
      69                 .WhereIf(input.ArticleCategoryId.HasValue, m => m.ArticleCategoryId == input.ArticleCategoryId.Value)
      70                 .WhereIf(!input.Keywords.IsNullOrWhiteSpace(), m => m.Title.Contains(input.Keywords));
      71 
      72             var result = await query.Query(input).ToAsync<ArticleItem>();
      73             return result;
      74         }
      75     }
      76 }

       

      ArticleController.cs代碼如下:

       1 namespace Fami.Mc.Web.Controllers
       2 {
       3     public class ArticleController : FamiControllerBase
       4     {
       5         private readonly IArticleAppService _articleAppService;
       6 
       7         public ArticleController(IArticleAppService articleAppService)
       8         {
       9             _articleAppService = articleAppService;
      10         }
      11 
      12         public async Task<ActionResult> Index()
      13         {
      14             ViewBag.ArticleCategoryDtos = await _articleAppService.GetArticleCategories();
      15             return View();
      16         }
      17 
      18         public async Task<ActionResult> Edit(Guid? id)
      19         {
      20             ArticleDto model;
      21             if (!id.HasValue)  //新建
      22             {
      23                 model = new ArticleDto();
      24                 ViewBag.ActionName = "createArticle";
      25             }
      26             else  //編輯
      27             {
      28                 model = await _articleAppService.GetArticle(id.Value);
      29                 ViewBag.ActionName = "updateArticle";
      30             }
      31             ViewBag.ArticleCategoryDtos = await _articleAppService.GetArticleCategories();
      32             return View(model);
      33         }
      34     }
      35 }

       

      Views/Article/Index.cshtml代碼(列表頁):

       1 <div class="page-content">
       2     <div class="page-header">
       3         <div class="page-title">文章管理</div>
       4         <!-- 過濾條件start -->
       5         <div id="filterbar" class="alert alert-lightsGray fs12 clearfix">
       6             <div class="clearfix" style="margin-right:30px;">
       7                 <div class="clearfix pull-left" style="line-height: 30px; margin: 3px 5px; ">
       8                     <div class="pull-left">分類:</div>
       9                     <div class="pull-left">
      10                         @Html.DropDownList("ArticleCategoryId", new SelectList(ViewBag.ArticleCategoryDtos, "Id", "CategoryName"), "", new { @class = "form-control w180"})
      11                     </div>
      12                 </div>
      13                 <div class="clearfix pull-left" style="line-height: 30px; margin: 3px 5px;">
      14                     <div class="pull-left">搜索:</div>
      15                     <div class="input-group input-group-sm w130">
      16                         <input class="form-control pull-left" placeholder="文章標題" filterfield="Keywords" name="Keywords" type="text">
      17                         <span class="input-group-btn">
      18                             <button class="btn btn-default btnSearch" type="button"><i class="icon-search2 fs14"></i></button>
      19                         </span>
      20                     </div>
      21                 </div>
      22             </div>
      23         </div>
      24         <!-- 過濾條件end -->
      25     </div>
      26 
      27     <!-- 列表上的功能按鈕放在這里 -->
      28     <div class="buttons-panel">
      29         <button id="btnNew" class="btn btn-primary"><i class="icon-plus2"></i>新增文章</button>
      30         <button id="btnEdit" class="btn btn-default"><i class="icon-edit"></i>編輯</button>
      31         <button id="btnDeletes" class="btn btn-default"><i class="icon-trash"></i>刪除 </button>
      32         <button id="btnReload" class="btn btn-default"><i class="icon-refresh"></i>刷新 </button>
      33     </div>
      34     <table id="mytable" class="wx-listview table table-bordered"></table>
      35 </div>
      36 @section js{
      37     @Scripts.Render("~/js/datatables")
      38     <script src="~/Areas/WechatMp/js/article.js"></script>
      39 }

       

      article.js代碼:

       1 var listColumns = [
       2         listCheckboxColumn,
       3         { "name": "id", "data": "id", title: "ID", "sortable": false, "visible": false },
       4         { "name": "title", "data": "title", title: "名稱" },
       5         {
       6             "name": "picUrl", "data": "picUrl", title: "圖片", "width": "100", "sortable": false,
       7             "render": function (data) { return '<img src="' + abp.resourcePath + data + '" style="width:60px;"/>';}
       8         },
       9         { "name": "articleCategoryCategoryName", "data": "articleCategoryCategoryName", title: "所屬分類" },
      10         { "name": "linkUrl", "data": "linkUrl", title: "外鏈地址" },
      11         { "name": "originalUrl", "data": "originalUrl", title: "原文地址" },
      12         { "name": "creationTime", "data": "creationTime", title: "創建時間", "width": "180" }
      13 ];
      14 
      15 $(function () {
      16     abp.grid.init({
      17             order: [[abp.grid.getColIndex("creationTime"), "desc"]],
      18             filterbar: "#filterbar",//過濾區域selector
      19             table: "#mytable",//table selector
      20             ajax: abp.grid.ajaxLoadEx({
      21                 "url": abp.appPath + "api/wechatmp/article/getArticleList",
      22             }),
      23             columns: listColumns
      24         });
      25 
      26     //新增
      27     $("#btnNew").click(function () {
      28         abp.dialog({
      29             width: "900px",
      30             title: "新增文章",
      31             href: abp.appPath + 'WechatMp/Article/Edit',
      32             callback: abp.grid.reloadList
      33         });
      34     });
      35 
      36     //編輯
      37     $("#btnEdit").on('click', function () {
      38         var row = abp.grid.getSelectedOneRowData();
      39         if (!row) return;
      40         abp.dialog({
      41             width: "900px",
      42             title: "編輯分類",
      43             href: abp.appPath + 'WechatMp/Article/Edit/' + row.id,
      44             callback: abp.grid.reloadList
      45         });
      46     });
      47 
      48     //刪除
      49     $("#btnDeletes").on('click', function () {
      50         var idList = abp.grid.getSelectedIdList();
      51         if (idList.length == 0) return;
      52 
      53         abp.confirm(abp.utils.formatString("您確認要刪除選中的{0}行嗎?", idList.length), function (result) {
      54             if (!result) return; //取消
      55             abp.ajax({
      56                 url: abp.appPath + 'api/wechatmp/article/batchDeleteArticle',
      57                 data: idList
      58             }).done(function (ret) {
      59                 abp.success("刪除成功");
      60                 abp.grid.reloadList();
      61             });
      62         });
      63     });
      64 })

       

      界面截圖:

       

      在進行這個列表查詢時,客戶端ajax直接調用ArticleAppService的GetArticleList方法,看下瀏覽器請求:

      會根據文章分類的下拉選項,自動生成ArticleCategoryId的查詢過濾參數。

       

       

      服務端執行GetArticleList方法,自動把客戶端ajax提交的數據組裝成input參數(GetArticleListInput類指定的結構),然后根據過濾條件進行查詢:

       1         /// <summary>
       2         /// 根據查詢條件,返回文章列表數據
       3         /// </summary>
       4         /// <param name="input">查詢條件</param>
       5         /// <returns></returns>
       6         public async Task<QueryResultOutput<ArticleItem>> GetArticleList(GetArticleListInput input)
       7         {
       8             var query = _articleRepository.GetAll()
       9                 .WhereIf(input.ArticleCategoryId.HasValue, m => m.ArticleCategoryId == input.ArticleCategoryId.Value)
      10                 .WhereIf(!input.Keywords.IsNullOrWhiteSpace(), m => m.Title.Contains(input.Keywords));
      11 
      12             var result = await query.Query(input).ToAsync<ArticleItem>();
      13             return result;
      14         }

       這個例子中僅過濾了ArticleCategoryId,沒有輸入標題中的關鍵字

       

      EF自動生成的SQL如下,只查ArticleItem類指定的字段,會自動關鍵文章分類表查取分類名稱,會自動根據當前登錄用戶的TenantId(租戶Id)來過濾。

      并且取總記錄數和取指定頁數據的兩步操作,僅會生成一條Sql語句在SqlServer中執行:

       

       1 exec sp_executesql N'-- Query #1
       2 
       3 SELECT 
       4     [GroupBy1].[A1] AS [C1]
       5     FROM ( SELECT 
       6         COUNT(1) AS [A1]
       7         FROM [dbo].[WechatMp_Article] AS [Extent1]
       8         WHERE (cast(''e5f2aea7-1423-4708-8162-7d029f5966d1'' as uniqueidentifier) = [Extent1].[TenantId]) AND ([Extent1].[ArticleCategoryId] = @f0_p__linq__0)
       9     )  AS [GroupBy1];
      10 
      11 -- Query #2
      12 
      13 SELECT TOP (10) 
      14     [Project1].[C1] AS [C1], 
      15     [Project1].[Title] AS [Title], 
      16     [Project1].[PicUrl] AS [PicUrl], 
      17     [Project1].[LinkUrl] AS [LinkUrl], 
      18     [Project1].[OriginalUrl] AS [OriginalUrl], 
      19     [Project1].[CategoryName] AS [CategoryName], 
      20     [Project1].[CreationTime] AS [CreationTime], 
      21     [Project1].[Id] AS [Id]
      22     FROM ( SELECT 
      23         [Extent1].[Id] AS [Id], 
      24         [Extent1].[Title] AS [Title], 
      25         [Extent1].[PicUrl] AS [PicUrl], 
      26         [Extent1].[LinkUrl] AS [LinkUrl], 
      27         [Extent1].[OriginalUrl] AS [OriginalUrl], 
      28         [Extent1].[CreationTime] AS [CreationTime], 
      29         [Extent2].[CategoryName] AS [CategoryName], 
      30         1 AS [C1]
      31         FROM  [dbo].[WechatMp_Article] AS [Extent1]
      32         INNER JOIN [dbo].[WechatMp_ArticleCategory] AS [Extent2] ON [Extent1].[ArticleCategoryId] = [Extent2].[Id]
      33         WHERE (cast(''e5f2aea7-1423-4708-8162-7d029f5966d1'' as uniqueidentifier) = [Extent1].[TenantId]) AND ([Extent1].[ArticleCategoryId] = @f1_p__linq__0)
      34     )  AS [Project1]
      35     ORDER BY [Project1].[CreationTime] DESC;
      36 ',N'@f0_p__linq__0 uniqueidentifier,@f1_p__linq__0 uniqueidentifier',@f0_p__linq__0='05506DBD-A0CB-449D-82F9-A462014C4440',@f1_p__linq__0='05506DBD-A0CB-449D-82F9-A462014C4440'

       

       

        

      由于這個功能實在太簡單,沒有使用到領域服務、領域事件,這里可能只能說明一件事件:沒有復雜業務邏輯的功能使用此DDD框架,并不會增加代碼量,反而我認為這樣的代碼量差不多已經少到極致了。

       

      真沒想到今晚又搞到這么晚,一篇文章寫了5個小時了,寫文章實在太慢了!有興趣的朋友還是互動討論吧。

       

      以后再對框架的每一種機制進行詳細說明。

       

      —————————————————————————————————————————————————————————————— 

      2015-3-23 13:10補充:

      下面貼一下框架層Repository基類的接口,為了顯示簡潔,我發到這里的代碼把注釋全去掉了,從方法名稱和參數很容易知道他們的作用,

      除返回IQueryable<TEntity>接口的GetAll()方法,其他都有同步和異步兩個版本。

        1 public interface IRepository<TEntity, TPrimaryKey> : IRepository where TEntity : class, IEntity<TPrimaryKey>
        2 {
        3     IQueryable<TEntity> GetAll();
        4 
        5     List<TEntity> GetAllList();
        6 
        7     Task<List<TEntity>> GetAllListAsync();
        8 
        9     List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate);
       10 
       11     Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate);
       12 
       13     TEntity Get(TPrimaryKey id);
       14 
       15     Task<TEntity> GetAsync(TPrimaryKey id);
       16 
       17     TEntity Single(Expression<Func<TEntity, bool>> predicate);
       18 
       19     Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate);
       20 
       21     TEntity FirstOrDefault(TPrimaryKey id);
       22 
       23     Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id);
       24 
       25     TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);
       26 
       27     Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
       28 
       29     TEntity Insert(TEntity entity);
       30 
       31     Task<TEntity> InsertAsync(TEntity entity);
       32 
       33     TPrimaryKey InsertAndGetId(TEntity entity);
       34 
       35     Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity);
       36 
       37     TEntity InsertOrUpdate(TEntity entity);
       38 
       39     Task<TEntity> InsertOrUpdateAsync(TEntity entity);
       40 
       41     TPrimaryKey InsertOrUpdateAndGetId(TEntity entity);
       42 
       43     Task<TPrimaryKey> InsertOrUpdateAndGetIdAsync(TEntity entity);
       44 
       45     TEntity Update(TEntity entity);
       46 
       47     Task<TEntity> UpdateAsync(TEntity entity);
       48 
       49     TEntity Update(TPrimaryKey id, Action<TEntity> updateAction);
       50 
       51     Task<TEntity> UpdateAsync(TPrimaryKey id, Func<TEntity, Task> updateAction);
       52 
       53     int BatchUpdate(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, TEntity>> updateExpression);
       54 
       55     Task<int> BatchUpdateAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, TEntity>> updateExpression);
       56 
       57     void BatchUpdateDisplayOrder(IEnumerable<TPrimaryKey> idList);  
       58 
       59     Task BatchUpdateDisplayOrderAsync(IEnumerable<TPrimaryKey> idList);
       60 
       61     void Delete(TEntity entity);
       62 
       63     Task DeleteAsync(TEntity entity);
       64 
       65     void Delete(TPrimaryKey id);
       66 
       67     Task DeleteAsync(TPrimaryKey id);
       68 
       69     void Delete(Expression<Func<TEntity, bool>> predicate);
       70 
       71     Task DeleteAsync(Expression<Func<TEntity, bool>> predicate);
       72 
       73     void Delete(IEnumerable<TPrimaryKey> idList);
       74 
       75     Task DeleteAsync(IEnumerable<TPrimaryKey> idList);
       76 
       77     void BatchDelete(Expression<Func<TEntity, bool>> predicate);
       78 
       79     Task BatchDeleteAsync(Expression<Func<TEntity, bool>> predicate);
       80 
       81     void BatchDelete(IEnumerable<TPrimaryKey> idList);
       82 
       83     Task BatchDeleteAsync(IEnumerable<TPrimaryKey> idList);
       84 
       85     int Count();
       86 
       87     Task<int> CountAsync();
       88 
       89     int Count(Expression<Func<TEntity, bool>> predicate);
       90 
       91     Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate);
       92 
       93     long LongCount();
       94 
       95     Task<long> LongCountAsync();
       96 
       97     long LongCount(Expression<Func<TEntity, bool>> predicate);
       98 
       99     Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate);
      100 }

       

      可能只有BatchUpdateDisplayOrder方法可能不太容易理解,我單獨說明一下:這個是列表頁面對表格行手動上下拖動排序后,根據idList傳入的Id及順序,更新DisplayOrder字段

      (只有在數據量不大,不需要分頁的情況下,才允許使用這種方式手動排序)

       

       

       

      --------------------------------------------------------------------------------------------------

      2015-3-23 15:40補充 回復@何鎮汐 多租戶機制的自動實現

      自動實現兩方面的操作:

      1、新建實體時自動從當前用戶的session中取出所屬的租戶標識(TenantId) 給實體的TenantId賦值

      2、查詢數據時自動根據當前用戶的TenantId過濾

       

      先說第1個,自動賦值的實現方式:

      拿本文上面的創建文章例子來說明

      ArticleAppService的CreateArticle方式主要代碼如下:

              public async Task<ArticleDto> CreateArticle(ArticleDto model)
              {
                  var entity = await _articleRepository.InsertAsync(model.MapTo<Article>());
                  return entity.MapTo<ArticleDto>();
              }

      CreateArticle方法中“model.MapTo<Article>()” 會自動創建Article實體類的實例(在基類的構造函數中自動生成Guid類型的Id),并將表單控件輸入的值(Dto類的屬性)賦值給新建的實體類,然后調用倉儲基類的Insert方法,這時并沒有提交到數據庫。因為框架會自動給CreateArticle方法應用UnitOfWork并開啟數據庫事務,當CreateArticle方法順利執行完畢(沒有拋出異常),會應用框架基類DbContext中的SaveChangesAsync方法,做一些自動賦值和事件觸發后再調用base.SaveChangesAsync

      請看代碼:

       1         public override int SaveChanges()
       2         {
       3             ApplyAbpConcepts();
       4             return base.SaveChanges();
       5         }
       6 
       7         public override Task<int> SaveChangesAsync(CancellationToken cancellationToken)
       8         {
       9             ApplyAbpConcepts();
      10             return base.SaveChangesAsync(cancellationToken);
      11         }
      12 
      13         private void ApplyAbpConcepts()
      14         {
      15             foreach (var entry in ChangeTracker.Entries())
      16             {
      17                 switch (entry.State)
      18                 {
      19                     case EntityState.Added:
      20                         SetCreationAuditProperties(entry);
      21                         EntityEventHelper.TriggerEntityCreatingEvent(entry.Entity);  // <-- 請看這里
      22                         EntityEventHelper.TriggerEntityCreatedEvent(entry.Entity);
      23                         break;
      24                     case EntityState.Modified:
      25                         if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
      26                         {
      27                             HandleSoftDelete(entry);
      28                             EntityEventHelper.TriggerEntityDeletedEvent(entry.Entity);
      29                         }
      30                         else
      31                         {
      32                             SetModificationAuditProperties(entry);
      33                             EntityEventHelper.TriggerEntityUpdatedEvent(entry.Entity);
      34                         }
      35                         break;
      36                     case EntityState.Deleted:
      37                         HandleSoftDelete(entry);
      38                         EntityEventHelper.TriggerEntityDeletedEvent(entry.Entity);
      39                         break;
      40                 }
      41             }
      42         }
      43 
      44         private void SetCreationAuditProperties(DbEntityEntry entry)
      45         {
      46             if (entry.Entity is IHasCreationTime)
      47             {
      48                 entry.Cast<IHasCreationTime>().Entity.CreationTime = DateTime.Now;
      49             }
      50 
      51             if (entry.Entity is ICreationAudited)
      52             {
      53                 entry.Cast<ICreationAudited>().Entity.CreatorUserId = AbpSession.UserId;
      54             }
      55         }
      56 
      57         private void SetModificationAuditProperties(DbEntityEntry entry)
      58         {
      59             if (entry.Entity is IModificationAudited)
      60             {
      61                 var auditedEntry = entry.Cast<IModificationAudited>();
      62 
      63                 auditedEntry.Entity.LastModificationTime = DateTime.Now;
      64                 auditedEntry.Entity.LastModifierUserId = AbpSession.UserId;
      65             }
      66         }
      67 
      68         private void HandleSoftDelete(DbEntityEntry entry)
      69         {
      70             if (entry.Entity is ISoftDelete)
      71             {
      72                 var softDeleteEntry = entry.Cast<ISoftDelete>();
      73 
      74                 softDeleteEntry.State = EntityState.Unchanged;
      75                 softDeleteEntry.Entity.IsDeleted = true;
      76 
      77                 if (entry.Entity is IDeletionAudited)
      78                 {
      79                     var deletionAuditedEntry = entry.Cast<IDeletionAudited>();
      80                     deletionAuditedEntry.Entity.DeletionTime = DateTime.Now;
      81                     deletionAuditedEntry.Entity.DeleterUserId = AbpSession.UserId;
      82                 }
      83             }
      84         }

       

      然后再看EntityEventHelper.TriggerEntityCreatingEvent的實現代碼:

      1         public void TriggerEntityCreatingEvent(object entity)
      2         {
      3             var entityType = entity.GetType();
      4             var eventType = typeof(EntityCreatingEventData<>).MakeGenericType(entityType);
      5             var eventData = (IEventData)Activator.CreateInstance(eventType, new[] { entity });
      6             EventBus.Trigger(eventType, eventData);
      7         }

      就是通過框架的EventBus觸發了一個事件,然后在Fami項目里捕獲這個事件:

       1     public class EntityCreatingEventHandler : IEventHandler<EntityCreatingEventData<Entity>>, ITransientDependency
       2     {
       3         private readonly IAbpSession _session;
       4 
       5         public EntityCreatingEventHandler(IAbpSession session)
       6         {
       7             _session = session;
       8         }
       9 
      10         public void HandleEvent(EntityCreatingEventData<Entity> eventData)
      11         {
      12             autoFillRelationId(eventData.Entity);
      13         }
      14 
      15         //新增實體時,自動填入關聯的TenantId、xxxxId
      16         private void autoFillRelationId(Entity entity)
      17         {
      18             if (entity is IMustHaveTenant)
      19 { 20 ((IMustHaveTenant)entity).TenantId = _session.GetTenantId();
      21 } 22 ...... //這里把其他代碼刪掉了 23 } 24 25 }

      這樣就自動賦值了,當然前提是這個實體實現了IMustHaveTenant接口,我寫了相應基類自動實現了這個接口。

      1     public interface IMustHaveTenant
      2     {
      3         Guid TenantId { get; set; }
      4     }
      1     public abstract class AuditedEntityAndTenant : AuditedEntity, IMustHaveTenant, IFilterByTenant
      2     {
      3         [Index]
      4         public virtual Guid TenantId { get; set; }
      5     }

       

      再說第2個,查詢時自動實現TenantId的過濾:

      已經有更新的方式實現,所以把以前的回答內容刪除了。

      現在用了EntityFramework.DynamicFilters組件實現自動過濾。

       

      posted @ 2015-03-23 02:03  陽光銘睿  閱讀(33706)  評論(104)    收藏  舉報
      主站蜘蛛池模板: 人妻少妇久久久久久97人妻| 美女又黄又免费的视频| 亚洲尤码不卡av麻豆| 18禁精品一区二区三区| 国产在线播放专区av| 黄色网站免费在线观看| 国产av一区二区不卡| 亚洲男人av天堂久久资源| 国产AV影片麻豆精品传媒| 精品国产精品国产偷麻豆| 白丝乳交内射一二三区| 中文字幕网红自拍偷拍视频| 亚洲欧洲色图片网站| 麻豆精产国品一二三区区| 婷婷四房播播| 青青草无码免费一二三区| 国产精品一区二区黄色片| 四虎在线成人免费观看| 亚洲综合色网一区二区三区| 精品免费看国产一区二区| 亚洲欧洲日韩国内精品| 日本极品少妇videossexhd| 欧美丰满熟妇vaideos| 国产成人av三级在线观看| 丰满的人妻hd高清日本| 国产不卡一区不卡二区| 奉节县| 国产精品va无码一区二区| 一级片免费网站| 亚洲日本va午夜在线影院| 国色天香中文字幕在线视频| 亚洲色成人一区二区三区人人澡人人妻人人爽人人蜜桃麻豆 | 欧美高清一区三区在线专区| 中文字幕免费不卡二区| 欧美激情精品久久| 久久天天躁狠狠躁夜夜躁2012| 亚洲开心婷婷中文字幕| 在线视频不卡在线亚洲| 办公室强奷漂亮少妇同事| 不卡乱辈伦在线看中文字幕| 国产91色综合久久免费|