大齡程式設計師談架構經驗 內行看門道

前言

孔乙己顯出極高興的樣子,將兩個指頭的長指甲敲著櫃檯,點頭說:“對呀,對呀!……回字有四樣寫法,你知道麼?”

大家好,我是44歲的大齡程式設計師碼農阿峰。阿峰從事程式設計二十年了,雖然沒有成為架構師,卻也用過很多種架構。我覺得一招鮮走遍天,架構師常用的那幾招我還是會的,聽我來說道說道。我以為至少有這幾招:

模板方法設計模式

反射

不重複造輪子,集眾家所長

架構經驗總結

1).模板方法設計模式的運用

在一個方法中定義了一個演算法的骨架或者步驟,而將一些步驟延遲到子類中去實現。模板方法使得子類可以在不改變演算法結構的情況下,重新定義演算法中的某一些步驟。

#父類: namespace Repository{ ///

/// 資料庫儲存泛型基類 /// /// public class BaseRepository : SimpleClient where T : class, new() { public ITenant itenant = null;//多租戶事務 public BaseRepository(ISqlSugarClient context = null) : base(context) { //透過特性拿到ConfigId var configId = typeof(T)。GetCustomAttribute()?。configId; if (configId != null) { Context = DbScoped。SugarScope。GetConnectionScope(configId);//根據類傳入的ConfigId自動選擇 } else { Context = context ?? DbScoped。SugarScope。GetConnectionScope(0);//沒有預設db0 } itenant = DbScoped。SugarScope;//設定租戶介面 } #region add /// /// 插入實體 /// /// /// public int Add(T t) { return Context。Insertable(t)。IgnoreColumns(true)。ExecuteCommand(); } public int Insert(List t) { return Context。Insertable(t)。ExecuteCommand(); } public int Insert(T parm, Expression> iClumns = null, bool ignoreNull = true) { return Context。Insertable(parm)。InsertColumns(iClumns)。IgnoreColumns(ignoreNullColumn: ignoreNull)。ExecuteCommand(); } public IInsertable Insertable(T t) { return Context。Insertable(t); } #endregion add #region update public IUpdateable Updateable(T entity) { return Context。Updateable(entity); } public int Update(T entity, bool ignoreNullColumns = false) { return Context。Updateable(entity)。IgnoreColumns(ignoreNullColumns)。ExecuteCommand(); } public int Update(T entity, Expression> expression, bool ignoreAllNull = false) { return Context。Updateable(entity)。UpdateColumns(expression)。IgnoreColumns(ignoreAllNull)。ExecuteCommand(); } /// /// 根據實體類更新指定列 eg:Update(dept, it => new { it。Status }, f => depts。Contains(f。DeptId));只更新Status列,條件是包含 /// /// /// /// /// public int Update(T entity, Expression> expression, Expression> where) { return Context。Updateable(entity)。UpdateColumns(expression)。Where(where)。ExecuteCommand(); } public int Update(SqlSugarClient client, T entity, Expression> expression, Expression> where) { return client。Updateable(entity)。UpdateColumns(expression)。Where(where)。ExecuteCommand(); } /// /// /// /// /// /// 預設為true /// public int Update(T entity, List list = null, bool isNull = true) { if (list == null) { list = new List() { “Create_By”, “Create_time” }; } return Context。Updateable(entity)。IgnoreColumns(isNull)。IgnoreColumns(list。ToArray())。ExecuteCommand(); } /// /// 更新指定列 eg:Update(w => w。NoticeId == model。NoticeId, it => new SysNotice(){ Update_time = DateTime。Now, Title = “通知標題” }); /// /// /// /// public int Update(Expression> where, Expression> columns) { return Context。Updateable()。SetColumns(columns)。Where(where)。RemoveDataCache()。ExecuteCommand(); } #endregion update public DbResult UseTran(Action action) { try { var result = Context。Ado。UseTran(() => action()); return result; } catch (Exception ex) { Context。Ado。RollbackTran(); Console。WriteLine(ex。Message); throw; } } public IStorageable Storageable(T t) { return Context。Storageable(t); } public IStorageable Storageable(List t) { return Context。Storageable(t); } /// /// /// /// /// 增刪改查方法 /// public DbResult UseTran(SqlSugarClient client, Action action) { try { var result = client。AsTenant()。UseTran(() => action()); return result; } catch (Exception ex) { client。AsTenant()。RollbackTran(); Console。WriteLine(ex。Message); throw; } } public bool UseTran2(Action action) { var result = Context。Ado。UseTran(() => action()); return result。IsSuccess; } #region delete public IDeleteable Deleteable() { return Context。Deleteable(); } /// /// 批次刪除 /// /// /// public int Delete(object[] obj) { return Context。Deleteable()。In(obj)。ExecuteCommand(); } public int Delete(object id) { return Context。Deleteable(id)。ExecuteCommand(); } public int DeleteTable() { return Context。Deleteable()。ExecuteCommand(); } public bool Truncate() { return Context。DbMaintenance。TruncateTable(); } #endregion delete #region query public bool Any(Expression> expression) { return Context。Queryable()。Where(expression)。Any(); } public ISugarQueryable Queryable() { return Context。Queryable(); } public (List, int) QueryableToPage(Expression> expression, int pageIndex = 0, int pageSize = 10) { int totalNumber = 0; var list = Context。Queryable()。Where(expression)。ToPageList(pageIndex, pageSize, ref totalNumber); return (list, totalNumber); } public (List, int) QueryableToPage(Expression> expression, string order, int pageIndex = 0, int pageSize = 10) { int totalNumber = 0; var list = Context。Queryable()。Where(expression)。OrderBy(order)。ToPageList(pageIndex, pageSize, ref totalNumber); return (list, totalNumber); } public (List, int) QueryableToPage(Expression> expression, Expression> orderFiled, string orderBy, int pageIndex = 0, int pageSize = 10) { int totalNumber = 0; if (orderBy。Equals(“DESC”, StringComparison。OrdinalIgnoreCase)) { var list = Context。Queryable()。Where(expression)。OrderBy(orderFiled, OrderByType。Desc)。ToPageList(pageIndex, pageSize, ref totalNumber); return (list, totalNumber); } else { var list = Context。Queryable()。Where(expression)。OrderBy(orderFiled, OrderByType。Asc)。ToPageList(pageIndex, pageSize, ref totalNumber); return (list, totalNumber); } } public List SqlQueryToList(string sql, object obj = null) { return Context。Ado。SqlQuery(sql, obj); } /// /// 根據主值查詢單條資料 /// /// 主鍵值 /// 泛型實體 public T GetId(object pkValue) { return Context。Queryable()。InSingle(pkValue); } /// /// 根據條件查詢分頁資料 /// /// /// /// public PagedInfo GetPages(Expression> where, PagerInfo parm) { var source = Context。Queryable()。Where(where); return source。ToPage(parm); } public PagedInfo GetPages(Expression> where, PagerInfo parm, Expression> order, OrderByType orderEnum = OrderByType。Asc) { var source = Context。Queryable()。Where(where)。OrderByIF(orderEnum == OrderByType。Asc, order, OrderByType。Asc)。OrderByIF(orderEnum == OrderByType。Desc, order, OrderByType。Desc); return source。ToPage(parm); } public PagedInfo GetPages(Expression> where, PagerInfo parm, Expression> order, string orderByType) { return GetPages(where, parm, order, orderByType == “desc” ? OrderByType。Desc : OrderByType。Asc); } /// /// 查詢所有資料(無分頁,請慎用) /// /// public List GetAll(bool useCache = false, int cacheSecond = 3600) { return Context。Queryable()。WithCacheIF(useCache, cacheSecond)。ToList(); } #endregion query /// /// 此方法不帶output返回值 /// var list = new List(); /// list。Add(new SugarParameter(ParaName, ParaValue)); input /// /// /// /// public DataTable UseStoredProcedureToDataTable(string procedureName, List parameters) { return Context。Ado。UseStoredProcedure()。GetDataTable(procedureName, parameters); } /// /// 帶output返回值 /// var list = new List(); /// list。Add(new SugarParameter(ParaName, ParaValue, true)); output /// list。Add(new SugarParameter(ParaName, ParaValue)); input /// /// /// /// public (DataTable, List) UseStoredProcedureToTuple(string procedureName, List parameters) { var result = (Context。Ado。UseStoredProcedure()。GetDataTable(procedureName, parameters), parameters); return result; } } /// /// 分頁查詢擴充套件 /// public static class QueryableExtension { /// /// 讀取列表 /// /// /// 查詢表單式 /// 分頁引數 /// public static PagedInfo ToPage(this ISugarQueryable source, PagerInfo parm) { var page = new PagedInfo(); var total = 0; page。PageSize = parm。PageSize; page。PageIndex = parm。PageNum; page。Result = source。OrderByIF(!string。IsNullOrEmpty(parm。Sort), $“{parm。Sort} {(parm。SortType。Contains(”desc“) ? ”desc“ : ”asc“)}”) 。ToPageList(parm。PageNum, parm。PageSize, ref total); page。TotalNum = total; return page; } }} #子類: [AppService(ServiceLifetime = LifeTime。Transient)] public class GenDemoRepository : BaseRepository { #region 業務邏輯程式碼 #endregion }

現在大家都學精了,都使用泛型基類來簡化重複的程式碼,標準的架構還是按經典的三層架構標準來搭建。這個是基本功,不用說太細。

2).反射

反射:是。Net Framework和。Net Core提供的一個幫助類庫,可以訪問dll的metadata,並且使用它。

大齡程式設計師談架構經驗 內行看門道

反射反射,程式設計師的快樂

///

/// 註冊引用程式域中所有有AppService標記的類的服務 /// /// public static void AddAppService(this IServiceCollection services) { //var assemblies = AppDomain。CurrentDomain。GetAssemblies(); string []cls = new string[] { “Topaut。Repository”, “Topaut。Service”, “Topaut。Tasks” }; foreach (var item in cls) { Assembly assembly = Assembly。Load(item); Register(services, assembly); } } private static void Register(IServiceCollection services, Assembly assembly) { foreach (var type in assembly。GetTypes()) { var serviceAttribute = type。GetCustomAttribute(); if (serviceAttribute != null) { var serviceType = serviceAttribute。ServiceType; //情況1 適用於依賴抽象程式設計,注意這裡只獲取第一個 if (serviceType == null && serviceAttribute。InterfaceServiceType) { serviceType = type。GetInterfaces()。FirstOrDefault(); } //情況2 不常見特殊情況下才會指定ServiceType,寫起來麻煩 if (serviceType == null) { serviceType = type; } switch (serviceAttribute。ServiceLifetime) { case LifeTime。Singleton: services。AddSingleton(serviceType, type); break; case LifeTime。Scoped: services。AddScoped(serviceType, type); break; case LifeTime。Transient: services。AddTransient(serviceType, type); break; default: services。AddTransient(serviceType, type); break; } //Console。WriteLine($“註冊:{serviceType}”); } else { //Console。WriteLine($“註冊:{serviceType}”); } } } }

官方各種Service注入IOC容器都是手寫,框架作者利用反射實現了減少大量繁瑣的固定寫法,對於普通專案而言,反射的效能損耗微不足道的。

3).不重複造輪子集眾家所長

有時程式設計師喜歡浪費生命去重複造輪子,如果是為了學習一次怎麼造輪子是值得肯定的。否則就是吃飽了沒事幹閒的。現在是最好的時代(好多開源元件可用),也是最壞的時代(行業太卷)。框架作者使用了糖果大資料的開源元件SqlSugar來實現多資料庫型別的支援,還有多租戶,讀寫分離,分表分庫這些資料庫層面的資料儲存方案。實現前後端分離參考了一些其他的優秀Vue實現的Admin框架。

線上體驗

官方文件:http://www。izhaorui。cn/doc

vue3。x版本體驗:http://www。izhaorui。cn/vue3

vue2。x版本體驗:http://www。izhaorui。cn/admin

賬號密碼:admin/123456

https://gitee。com/izory/ZrAdminNetCore/