這應該是本系統最後一次重構,將重構BLL層和Model層。來徹底取代代碼生成器生成的BLL層和DAL層。徹底廢掉了代碼生成器的DAL,BLL,MODEL層。html
全自動生成增,刪,改,查的通用方法和模型轉換與BLL層的模型事務脫離,後續文章,會以一些插件或功能爲目的,繼續完善,進行分享,最後60節的文章會對本系統作一個總結前端
(可是還沒時間寫,相信60節的文章能讓你快速瞭解到本系統的優點和架構,就算你從未閱讀以前的全部文章)架構
繼上次的DAL層重構(上一節),原本只想重構DAL層算了,可是鑑於本人是代碼強迫症患者,因此花了些時間把BLL層重構。app
在此務必共鳴一個問題,代碼重構不是架構改變,這個系統的架構徹底仍是原來的接口多層注入架構!以下圖所示徹底不變ide
最後必須讓初學者理解一個知識點:分部類 partial 關鍵字,由於咱們的重構是圍繞分部類而實現,包括接口ui
partial 關鍵字指示可在命名空間中定義該類、結構或接口的其餘部分。全部部分都必須使用 partial 關鍵字。在編譯時,各個部分都必須可用來造成最終的類型。各個部分必須具備相同的可訪問性,如 public、private 等。this
若是將任意部分聲明爲抽象的,則整個類型都被視爲抽象的。若是將任意部分聲明爲密封的,則整個類型都被視爲密封的。若是任意部分聲明基類型,則整個類型都將繼承該類。spa
指定基類的全部部分必須一致,但忽略基類的部分仍繼承該基類型。各個部分能夠指定不一樣的基接口,最終類型將實現全部分部聲明所列出的所有接口。在某一分部定義中聲明的任何類、結構或接口成員可供全部其餘部分使用。最終類型是全部部分在編譯時的組合。插件
下列聲明:debug
partial class Earth : Planet, IRotate { } partial class Earth : IRevolve { }
等效於下列聲明:
class Earth : Planet, IRotate, IRevolve { }
相比咱們DAL層,重構BLL層是有技術難度的,由於業務層涉及模型的轉換構成,雖然只重構模塊的(增、刪、改、查),下面咱們開始
下載上一節代碼(https://yunpan.cn/cYUdjssbmiLrL 訪問密碼 e622)來分析業務層。
分析:IBLL,BLL
IBLL層不用說了,跟IDAL層是一致的
因此咱們直接複製IDAL的TT模版修改後以下
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="../../Apps.Models/Common.ttinclude"#><#@ output extension=".cs"#> <# const string inputFile = @"../../Apps.Models/DB.edmx"; var textTransform = DynamicTextTransformation.Create(this); var code = new CodeGenerationTools(this); var ef = new MetadataTools(this); var typeMapper = new TypeMapper(code, ef, textTransform.Errors); var fileManager = EntityFrameworkTemplateFileManager.Create(this); var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) { return string.Empty; } WriteHeader(codeStringGenerator, fileManager); foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection)) { fileManager.StartNewFile("I"+entity.Name + "BLL.cs"); #> using System; using Apps.Common; using System.Collections.Generic; using Apps.Models<#=entity.Name.IndexOf("_")>0?"."+entity.Name.Substring(0,entity.Name.IndexOf("_")):".Sys" #>; namespace Apps.IBLL { public partial interface I<#=entity.Name #>BLL { List<<#=entity.Name #>Model> GetList(ref GridPager pager, string queryStr); bool Create(ref ValidationErrors errors, <#=entity.Name #>Model model); bool Delete(ref ValidationErrors errors, string id); bool Delete(ref ValidationErrors errors, string[] deleteCollection); bool Edit(ref ValidationErrors errors, <#=entity.Name #>Model model); <#=entity.Name #>Model GetById(string id); bool IsExists(string id); } <# EndNamespace(code); } fileManager.Process(); #>
很是好。業務層完成跟預期是同樣的!這樣咱們直接能夠看到咱們原來的ISysSample能夠由
using System.Collections.Generic; using Apps.Common; using Apps.Models.Sys; namespace Apps.IBLL { public interface ISysSampleBLL { List<SysSampleModel> GetList(ref GridPager pager, string queryStr); bool Create(ref ValidationErrors errors, SysSampleModel model); bool Delete(ref ValidationErrors errors, string id); bool Delete(ref ValidationErrors errors, string[] deleteCollection); bool Edit(ref ValidationErrors errors, SysSampleModel model); SysSampleModel GetById(string id); bool IsExist(string id); } }
變爲--->
using System.Collections.Generic; using Apps.Common; using Apps.Models.Sys; namespace Apps.IBLL { public partial interface ISysSampleBLL { } }
代碼行數發生質的改變,能夠咱們就能夠擴展本身的接口方法,利用partial類
照樣畫葫蘆,業務層也生成
直接上TT代碼
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="../../Apps.Models/Common.ttinclude"#><#@ output extension=".cs"#> <# const string usingName = ""; const string inputFile = @"../../Apps.Models/DB.edmx"; var textTransform = DynamicTextTransformation.Create(this); var code = new CodeGenerationTools(this); var ef = new MetadataTools(this); var typeMapper = new TypeMapper(code, ef, textTransform.Errors); var fileManager = EntityFrameworkTemplateFileManager.Create(this); var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) { return string.Empty; } WriteHeader(codeStringGenerator, fileManager); foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection)) { if(entity.Name.StartsWith("Sys") || entity.Name.StartsWith("JOB")) { fileManager.StartNewFile(""+entity.Name + "BLL.cs"); var simpleProperties = typeMapper.GetSimpleProperties(entity); #> using System; using System.Collections.Generic; using System.Linq; using Apps.Models; using Apps.Common; using Microsoft.Practices.Unity; using System.Transactions; using Apps.IBLL; using Apps.IDAL; using Apps.BLL.Core; using Apps.Locale; using Apps.Models<#=entity.Name.IndexOf("_")>0?"."+entity.Name.Substring(0,entity.Name.IndexOf("_")):".Sys" #>; namespace Apps.BLL { public partial class <#=entity.Name #>BLL:I<#=entity.Name #>BLL { [Dependency] public I<#=entity.Name #>Repository m_Rep { get; set; } public virtual List<<#=entity.Name #>Model> GetList(ref GridPager pager, string queryStr) { IQueryable<<#=entity.Name #>> queryData = null; if (!string.IsNullOrWhiteSpace(queryStr)) { queryData = m_Rep.GetList( <# int i =0; if (simpleProperties.Any()){foreach(var edmProperty in simpleProperties){ if(i==0) { #> <#=codeStringGenerator.Property(edmProperty).ToString().IndexOf("string")>0?"a=>a."+edmProperty+".Contains(queryStr)":""#> <# if(codeStringGenerator.Property(edmProperty).ToString().IndexOf("string")>0) { i=1; } } else if(i==1) {#> <#=codeStringGenerator.Property(edmProperty).ToString().IndexOf("string")>0?"|| a."+edmProperty+".Contains(queryStr)":""#> <# } #> <#} }#> ); } else { queryData = m_Rep.GetList(); } pager.totalRows = queryData.Count(); //排序 queryData = LinqHelper.SortingAndPaging(queryData, pager.sort, pager.order, pager.page, pager.rows); return CreateModelList(ref queryData); } public virtual List<<#=entity.Name #>Model> CreateModelList(ref IQueryable<<#=entity.Name #>> queryData) { List<<#=entity.Name #>Model> modelList = (from r in queryData select new <#=entity.Name #>Model { <# if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> <#=edmProperty#> = r.<#=edmProperty#>, <# } } #> }).ToList(); return modelList; } public virtual bool Create(ref ValidationErrors errors, <#=entity.Name #>Model model) { try { <#=entity.Name #> entity = m_Rep.GetById(model.Id); if (entity != null) { errors.Add(Resource.PrimaryRepeat); return false; } entity = new <#=entity.Name #>(); <# if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> entity.<#=edmProperty#> = model.<#=edmProperty#>; <# } } #> if (m_Rep.Create(entity)) { return true; } else { errors.Add(Resource.InsertFail); return false; } } catch (Exception ex) { errors.Add(ex.Message); ExceptionHander.WriteException(ex); return false; } } public virtual bool Delete(ref ValidationErrors errors, string id) { try { if (m_Rep.Delete(id) == 1) { return true; } else { return false; } } catch (Exception ex) { errors.Add(ex.Message); ExceptionHander.WriteException(ex); return false; } } public virtual bool Delete(ref ValidationErrors errors, string[] deleteCollection) { try { if (deleteCollection != null) { using (TransactionScope transactionScope = new TransactionScope()) { if (m_Rep.Delete(deleteCollection) == deleteCollection.Length) { transactionScope.Complete(); return true; } else { Transaction.Current.Rollback(); return false; } } } return false; } catch (Exception ex) { errors.Add(ex.Message); ExceptionHander.WriteException(ex); return false; } } public virtual bool Edit(ref ValidationErrors errors, <#=entity.Name #>Model model) { try { <#=entity.Name #> entity = m_Rep.GetById(model.Id); if (entity == null) { errors.Add(Resource.Disable); return false; } <# if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> entity.<#=edmProperty#> = model.<#=edmProperty#>; <# } } #> if (m_Rep.Edit(entity)) { return true; } else { errors.Add("沒有數據改變"); return false; } } catch (Exception ex) { errors.Add(ex.Message); ExceptionHander.WriteException(ex); return false; } } public virtual <#=entity.Name #>Model GetById(string id) { if (IsExists(id)) { <#=entity.Name #> entity = m_Rep.GetById(id); <#=entity.Name #>Model model = new <#=entity.Name #>Model(); <# if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> model.<#=edmProperty#> = entity.<#=edmProperty#>; <# } } #> return model; } else { return null; } } public virtual bool IsExists(string id) { return m_Rep.IsExist(id); } public void Dispose() { } } <# EndNamespace(code); } } fileManager.Process(); #>
因爲每個業務模型的屬性都不一致,這裏不能用List<T>來作,因此,一個表會生成一個BLL類。(圖中紅色部分)
若是生成紅色部分。主要看下面代碼
<# if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> <#=edmProperty#> = r.<#=edmProperty#>, <# } } #>
獲取表模型的全部屬性,全部這段對不少人是有幫助的,請收藏,說不定你之後要用到
OK,編譯經過,運行正確,仍是熟悉的面孔
可是至此。咱們的業務層和數據訪問層,能夠說是一行代碼都沒寫。足夠體現了TT模版的強大之處,相比咱們以前要用代碼生成器來得極其方便
直到上面步驟,一切都很順利,沒有一點不妥。
有經驗的園友會發現,裏面東西都是寫死的。並且分部類不能夠重寫本身。
好比說。我在處理 entity.Name = model.Name;時候我想entity.Name = model.Name.TrimStart() 去掉字符串前面的空格,那麼能夠看到根本沒法操做。
然而咱們須要重寫,可是又發現沒法重寫分部類的方法,怎麼作?必須用一張圖來看,我是這麼作的
虛方法是能夠重寫的關鍵字是virtual 如下重寫以後優先級高於前者 用override。用代碼來講明
改變一下CommonBLL.tt
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="../../Apps.Models/Common.ttinclude"#><#@ output extension=".cs"#> <# const string usingName = ""; const string inputFile = @"../../Apps.Models/DB.edmx"; var textTransform = DynamicTextTransformation.Create(this); var code = new CodeGenerationTools(this); var ef = new MetadataTools(this); var typeMapper = new TypeMapper(code, ef, textTransform.Errors); var fileManager = EntityFrameworkTemplateFileManager.Create(this); var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) { return string.Empty; } WriteHeader(codeStringGenerator, fileManager); foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection)) { if(entity.Name.StartsWith("Sys") || entity.Name.StartsWith("JOB")) { fileManager.StartNewFile("Virtual_"+entity.Name + "BLL.cs"); var simpleProperties = typeMapper.GetSimpleProperties(entity); #> using System; using System.Collections.Generic; using System.Linq; using Apps.Models; using Apps.Common; using Microsoft.Practices.Unity; using System.Transactions; using Apps.IBLL; using Apps.IDAL; using Apps.BLL.Core; using Apps.Locale; using Apps.Models<#=entity.Name.IndexOf("_")>0?"."+entity.Name.Substring(0,entity.Name.IndexOf("_")):".Sys" #>; namespace Apps.BLL { public class Virtual_<#=entity.Name #>BLL { [Dependency] public I<#=entity.Name #>Repository m_Rep { get; set; } public virtual List<<#=entity.Name #>Model> GetList(ref GridPager pager, string queryStr) { IQueryable<<#=entity.Name #>> queryData = null; if (!string.IsNullOrWhiteSpace(queryStr)) { queryData = m_Rep.GetList( <# int i =0; if (simpleProperties.Any()){foreach(var edmProperty in simpleProperties){ if(i==0) { #> <#=codeStringGenerator.Property(edmProperty).ToString().IndexOf("string")>0?"a=>a."+edmProperty+".Contains(queryStr)":""#> <# if(codeStringGenerator.Property(edmProperty).ToString().IndexOf("string")>0) { i=1; } } else if(i==1) {#> <#=codeStringGenerator.Property(edmProperty).ToString().IndexOf("string")>0?"|| a."+edmProperty+".Contains(queryStr)":""#> <# } #> <#} }#> ); } else { queryData = m_Rep.GetList(); } pager.totalRows = queryData.Count(); //排序 queryData = LinqHelper.SortingAndPaging(queryData, pager.sort, pager.order, pager.page, pager.rows); return CreateModelList(ref queryData); } public virtual List<<#=entity.Name #>Model> CreateModelList(ref IQueryable<<#=entity.Name #>> queryData) { List<<#=entity.Name #>Model> modelList = (from r in queryData select new <#=entity.Name #>Model { <# if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> <#=edmProperty#> = r.<#=edmProperty#>, <# } } #> }).ToList(); return modelList; } public virtual bool Create(ref ValidationErrors errors, <#=entity.Name #>Model model) { try { <#=entity.Name #> entity = m_Rep.GetById(model.Id); if (entity != null) { errors.Add(Resource.PrimaryRepeat); return false; } entity = new <#=entity.Name #>(); <# if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> entity.<#=edmProperty#> = model.<#=edmProperty#>; <# } } #> if (m_Rep.Create(entity)) { return true; } else { errors.Add(Resource.InsertFail); return false; } } catch (Exception ex) { errors.Add(ex.Message); ExceptionHander.WriteException(ex); return false; } } public virtual bool Delete(ref ValidationErrors errors, string id) { try { if (m_Rep.Delete(id) == 1) { return true; } else { return false; } } catch (Exception ex) { errors.Add(ex.Message); ExceptionHander.WriteException(ex); return false; } } public virtual bool Delete(ref ValidationErrors errors, string[] deleteCollection) { try { if (deleteCollection != null) { using (TransactionScope transactionScope = new TransactionScope()) { if (m_Rep.Delete(deleteCollection) == deleteCollection.Length) { transactionScope.Complete(); return true; } else { Transaction.Current.Rollback(); return false; } } } return false; } catch (Exception ex) { errors.Add(ex.Message); ExceptionHander.WriteException(ex); return false; } } public virtual bool Edit(ref ValidationErrors errors, <#=entity.Name #>Model model) { try { <#=entity.Name #> entity = m_Rep.GetById(model.Id); if (entity == null) { errors.Add(Resource.Disable); return false; } <# if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> entity.<#=edmProperty#> = model.<#=edmProperty#>; <# } } #> if (m_Rep.Edit(entity)) { return true; } else { errors.Add(Resource.NoDataChange); return false; } } catch (Exception ex) { errors.Add(ex.Message); ExceptionHander.WriteException(ex); return false; } } public virtual <#=entity.Name #>Model GetById(string id) { if (IsExists(id)) { <#=entity.Name #> entity = m_Rep.GetById(id); <#=entity.Name #>Model model = new <#=entity.Name #>Model(); <# if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> model.<#=edmProperty#> = entity.<#=edmProperty#>; <# } } #> return model; } else { return null; } } public virtual bool IsExists(string id) { return m_Rep.IsExist(id); } public void Dispose() { } } <# EndNamespace(code); } } fileManager.Process(); #>
更Common代碼基本一致,只是頭部變了,文件名稱變了
public class Virtual_SysSampleBLL
那麼從新建立一個CommonBLL.tt
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="../../Apps.Models/Common.ttinclude"#><#@ output extension=".cs"#> <# const string usingName = ""; const string inputFile = @"../../Apps.Models/DB.edmx"; var textTransform = DynamicTextTransformation.Create(this); var code = new CodeGenerationTools(this); var ef = new MetadataTools(this); var typeMapper = new TypeMapper(code, ef, textTransform.Errors); var fileManager = EntityFrameworkTemplateFileManager.Create(this); var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) { return string.Empty; } WriteHeader(codeStringGenerator, fileManager); foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection)) { if(entity.Name.StartsWith("Sys") || entity.Name.StartsWith("JOB")) { fileManager.StartNewFile(entity.Name + "BLL.cs"); var simpleProperties = typeMapper.GetSimpleProperties(entity); #> using System; using System.Collections.Generic; using System.Linq; using Apps.Models; using Apps.Common; using Microsoft.Practices.Unity; using System.Transactions; using Apps.IBLL; using Apps.IDAL; using Apps.BLL.Core; using Apps.Locale; using Apps.Models<#=entity.Name.IndexOf("_")>0?"."+entity.Name.Substring(0,entity.Name.IndexOf("_")):".Sys" #>; namespace Apps.BLL { public partial class <#=entity.Name #>BLL: Virtual_<#=entity.Name #>BLL,I<#=entity.Name #>BLL { } <# EndNamespace(code); } } fileManager.Process(); #>
代碼生成後以下,什麼都沒有實現繼承接口,和上面的TT模版的類
namespace Apps.BLL { public partial class SysSampleBLL: Virtual_SysSampleBLL,ISysSampleBLL { } }
好吧,我只是想省掉寫: Virtual_SysSampleBLL,ISysSampleBLL
OK,運行以後仍是熟悉的面孔,可是能夠重載了,咱們重載一下,好處理咱們的業務!
新建SysSampleBLL.cs
namespace Apps.BLL { public partial class SysSampleBLL { public override bool Create(ref ValidationErrors errors, SysSampleModel model) { try { SysSample entity = m_Rep.GetById(model.Id); if (entity != null) { errors.Add(Resource.PrimaryRepeat); return false; } entity = new SysSample(); entity.Id = model.Id; entity.Name = model.Name.TrimStart(); entity.Age = model.Age; entity.Bir = model.Bir; entity.Photo = model.Photo; entity.Note = model.Note; entity.CreateTime = model.CreateTime; if (m_Rep.Create(entity)) { return true; } else { errors.Add(Resource.InsertFail); return false; } } catch (Exception ex) { errors.Add(ex.Message); ExceptionHander.WriteException(ex); return false; } } } }
一樣的。咱們能夠對Model層進行重構,相似BLL層。利用虛屬性,能夠對屬性進行註解。來得到優先級,和一次生成編譯經過
//------------------------------------------------------------------------------ // <auto-generated> // 此代碼已從模板生成。 // // 手動更改此文件可能致使應用程序出現意外的行爲。 // 若是從新生成代碼,將覆蓋對此文件的手動更改。 // </auto-generated> //------------------------------------------------------------------------------ using Apps.Models; using System; namespace Apps.Models.Sys { public class Virtual_SysSampleModel { public virtual string Id { get; set; } public virtual string Name { get; set; } public virtual Nullable<int> Age { get; set; } public virtual Nullable<System.DateTime> Bir { get; set; } public virtual string Photo { get; set; } public virtual string Note { get; set; } public virtual Nullable<System.DateTime> CreateTime { get; set; } } }
//------------------------------------------------------------------------------ // <auto-generated> // 此代碼已從模板生成。 // // 手動更改此文件可能致使應用程序出現意外的行爲。 // 若是從新生成代碼,將覆蓋對此文件的手動更改。 // </auto-generated> //------------------------------------------------------------------------------ using Apps.Models; using System; namespace Apps.Models.Sys { public partial class SysSampleModel:Virtual_SysSampleModel { } }
而後本身建Model對其重載
-------------------------------------------------------------------醜陋的分割線----------------------------------------------------------------------------------------
到此,咱們重構了DAL層和BLL層。對比原來的代碼生成器方式。咱們新建一個表不用再生成DAL層和BLL層的代碼。直達界面
利用代碼生成器得到控制器和View視圖。直接獲得界面。一個字爽。你們能夠下載代碼來研究
代碼生成器在第一節下載,可是代碼生成器本人好久沒有維護,可能生成的index.cshtml會有一些問題,可是好很好解決。本身花點時間來設計成本身的前端生成器。
OK、本文到此結束,謝謝