7

大龄程序员谈架构经验 内行看门道 - 数据酷软件

 2 years ago
source link: https://www.cnblogs.com/datacool/p/16794967.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

原创2022-10-15 17:01·码农阿峰

孔乙己显出极高兴的样子,将两个指头的长指甲敲着柜台,点头说:“对呀,对呀!......回字有四样写法,你知道么?”

大家好,我是44岁的大龄程序员码农阿峰。阿峰从事编程二十年了,虽然没有成为架构师,却也用过很多种架构。我觉得一招鲜走遍天,架构师常用的那几招我还是会的,听我来说道说道。我以为至少有这几招:

  1. 模板方法设计模式
  2. 不重复造轮子,集众家所长

架构经验总结

1).模板方法设计模式的运用

在一个方法中定义了一个算法的骨架或者步骤,而将一些步骤延迟到子类中去实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某一些步骤。

   #父类:
   
namespace Repository
{
    /// <summary>
    /// 数据库存储泛型基类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class BaseRepository<T> : SimpleClient<T> where T : class, new()
    {
        public ITenant itenant = null;//多租户事务
        public BaseRepository(ISqlSugarClient context = null) : base(context)
        {
            //通过特性拿到ConfigId
            var configId = typeof(T).GetCustomAttribute<TenantAttribute>()?.configId;
            if (configId != null)
            {
                Context = DbScoped.SugarScope.GetConnectionScope(configId);//根据类传入的ConfigId自动选择
            }
            else
            {
                Context = context ?? DbScoped.SugarScope.GetConnectionScope(0);//没有默认db0
            } 
            itenant = DbScoped.SugarScope;//设置租户接口
        }

        #region add

        /// <summary>
        /// 插入实体
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public int Add(T t)
        {
            return Context.Insertable(t).IgnoreColumns(true).ExecuteCommand();
        }

        public int Insert(List<T> t)
        {
            return Context.Insertable(t).ExecuteCommand();
        }
        public int Insert(T parm, Expression<Func<T, object>> iClumns = null, bool ignoreNull = true)
        {
            return Context.Insertable(parm).InsertColumns(iClumns).IgnoreColumns(ignoreNullColumn: ignoreNull).ExecuteCommand();
        }
        public IInsertable<T> Insertable(T t)
        {
            return Context.Insertable<T>(t);
        }
        #endregion add

        #region update
        public IUpdateable<T> 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<Func<T, object>> expression, bool ignoreAllNull = false)
        {
            return Context.Updateable(entity).UpdateColumns(expression).IgnoreColumns(ignoreAllNull).ExecuteCommand();
        }

        /// <summary>
        /// 根据实体类更新指定列 eg:Update(dept, it => new { it.Status }, f => depts.Contains(f.DeptId));只更新Status列,条件是包含
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="expression"></param>
        /// <param name="where"></param>
        /// <returns></returns>
        public int Update(T entity, Expression<Func<T, object>> expression, Expression<Func<T, bool>> where)
        {
            return Context.Updateable(entity).UpdateColumns(expression).Where(where).ExecuteCommand();
        }

        public int Update(SqlSugarClient client, T entity, Expression<Func<T, object>> expression, Expression<Func<T, bool>> where)
        {
            return client.Updateable(entity).UpdateColumns(expression).Where(where).ExecuteCommand();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="list"></param>
        /// <param name="isNull">默认为true</param>
        /// <returns></returns>
        public int Update(T entity, List<string> list = null, bool isNull = true)
        {
            if (list == null)
            {
                list = new List<string>()
            {
                "Create_By",
                "Create_time"
            };
            }
            return Context.Updateable(entity).IgnoreColumns(isNull).IgnoreColumns(list.ToArray()).ExecuteCommand();
        }
      
        /// <summary>
        /// 更新指定列 eg:Update(w => w.NoticeId == model.NoticeId, it => new SysNotice(){ Update_time = DateTime.Now, Title = "通知标题" });
        /// </summary>
        /// <param name="where"></param>
        /// <param name="columns"></param>
        /// <returns></returns>
        public int Update(Expression<Func<T, bool>> where, Expression<Func<T, T>> columns)
        {
            return Context.Updateable<T>().SetColumns(columns).Where(where).RemoveDataCache().ExecuteCommand();
        }
        #endregion update

        public DbResult<bool> 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<T> Storageable(T t)
        {
            return Context.Storageable<T>(t);
        }
        public IStorageable<T> Storageable(List<T> t)
        {
            return Context.Storageable(t);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="client"></param>
        /// <param name="action">增删改查方法</param>
        /// <returns></returns>
        public DbResult<bool> 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<T> Deleteable()
        {
            return Context.Deleteable<T>();
        }

        /// <summary>
        /// 批量删除
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int Delete(object[] obj)
        {
            return Context.Deleteable<T>().In(obj).ExecuteCommand();
        }
        public int Delete(object id)
        {
            return Context.Deleteable<T>(id).ExecuteCommand();
        }
        public int DeleteTable()
        {
            return Context.Deleteable<T>().ExecuteCommand();
        }
        public bool Truncate()
        {
            return Context.DbMaintenance.TruncateTable<T>();
        }
        #endregion delete

        #region query

        public bool Any(Expression<Func<T, bool>> expression)
        {
            return Context.Queryable<T>().Where(expression).Any();
        }

        public ISugarQueryable<T> Queryable()
        {
            return Context.Queryable<T>();
        }

        public (List<T>, int) QueryableToPage(Expression<Func<T, bool>> expression, int pageIndex = 0, int pageSize = 10)
        {
            int totalNumber = 0;
            var list = Context.Queryable<T>().Where(expression).ToPageList(pageIndex, pageSize, ref totalNumber);
            return (list, totalNumber);
        }

        public (List<T>, int) QueryableToPage(Expression<Func<T, bool>> expression, string order, int pageIndex = 0, int pageSize = 10)
        {
            int totalNumber = 0;
            var list = Context.Queryable<T>().Where(expression).OrderBy(order).ToPageList(pageIndex, pageSize, ref totalNumber);
            return (list, totalNumber);
        }

        public (List<T>, int) QueryableToPage(Expression<Func<T, bool>> expression, Expression<Func<T, object>> orderFiled, string orderBy, int pageIndex = 0, int pageSize = 10)
        {
            int totalNumber = 0;

            if (orderBy.Equals("DESC", StringComparison.OrdinalIgnoreCase))
            {
                var list = Context.Queryable<T>().Where(expression).OrderBy(orderFiled, OrderByType.Desc).ToPageList(pageIndex, pageSize, ref totalNumber);
                return (list, totalNumber);
            }
            else
            {
                var list = Context.Queryable<T>().Where(expression).OrderBy(orderFiled, OrderByType.Asc).ToPageList(pageIndex, pageSize, ref totalNumber);
                return (list, totalNumber);
            }
        }

        public List<T> SqlQueryToList(string sql, object obj = null)
        {
            return Context.Ado.SqlQuery<T>(sql, obj);
        }

        /// <summary>
        /// 根据主值查询单条数据
        /// </summary>
        /// <param name="pkValue">主键值</param>
        /// <returns>泛型实体</returns>
        public T GetId(object pkValue)
        {
            return Context.Queryable<T>().InSingle(pkValue);
        }
        /// <summary>
        /// 根据条件查询分页数据
        /// </summary>
        /// <param name="where"></param>
        /// <param name="parm"></param>
        /// <returns></returns>
        public PagedInfo<T> GetPages(Expression<Func<T, bool>> where, PagerInfo parm)
        {
            var source = Context.Queryable<T>().Where(where);

            return source.ToPage(parm);
        }

        public PagedInfo<T> GetPages(Expression<Func<T, bool>> where, PagerInfo parm, Expression<Func<T, object>> order, OrderByType orderEnum = OrderByType.Asc)
        {
            var source = Context.Queryable<T>().Where(where).OrderByIF(orderEnum == OrderByType.Asc, order, OrderByType.Asc).OrderByIF(orderEnum == OrderByType.Desc, order, OrderByType.Desc);

            return source.ToPage(parm);
        }

        public PagedInfo<T> GetPages(Expression<Func<T, bool>> where, PagerInfo parm, Expression<Func<T, object>> order, string orderByType)
        {
            return GetPages(where, parm, order, orderByType == "desc" ? OrderByType.Desc : OrderByType.Asc);
        }

        /// <summary>
        /// 查询所有数据(无分页,请慎用)
        /// </summary>
        /// <returns></returns>
        public List<T> GetAll(bool useCache = false, int cacheSecond = 3600)
        {
            return Context.Queryable<T>().WithCacheIF(useCache, cacheSecond).ToList();
        }

        #endregion query

        /// <summary>
        /// 此方法不带output返回值
        /// var list = new List<SugarParameter>();
        /// list.Add(new SugarParameter(ParaName, ParaValue)); input
        /// </summary>
        /// <param name="procedureName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public DataTable UseStoredProcedureToDataTable(string procedureName, List<SugarParameter> parameters)
        {
            return Context.Ado.UseStoredProcedure().GetDataTable(procedureName, parameters);
        }

        /// <summary>
        /// 带output返回值
        /// var list = new List<SugarParameter>();
        /// list.Add(new SugarParameter(ParaName, ParaValue, true));  output
        /// list.Add(new SugarParameter(ParaName, ParaValue)); input
        /// </summary>
        /// <param name="procedureName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public (DataTable, List<SugarParameter>) UseStoredProcedureToTuple(string procedureName, List<SugarParameter> parameters)
        {
            var result = (Context.Ado.UseStoredProcedure().GetDataTable(procedureName, parameters), parameters);
            return result;
        }
    }

    /// <summary>
    /// 分页查询扩展
    /// </summary>
    public static class QueryableExtension
    {
        /// <summary>
        /// 读取列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">查询表单式</param>
        /// <param name="parm">分页参数</param>
        /// <returns></returns>
        public static PagedInfo<T> ToPage<T>(this ISugarQueryable<T> source, PagerInfo parm)
        {
            var page = new PagedInfo<T>();
            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<GenDemo>
    {
        #region 业务逻辑代码
        #endregion
    }

现在大家都学精了,都使用泛型基类来简化重复的代码,标准的架构还是按经典的三层架构标准来搭建。这个是基本功,不用说太细。

2).反射

反射:是.Net Framework和.Net Core提供的一个帮助类库,可以访问dll的metadata,并且使用它。

39619003338c4b6c82231aa7c6bf477d~noop.image?_iz=58558&from=article.pc_detail&x-expires=1666441500&x-signature=G%2Bjx35erYLIBT55d7ydxjzYnAU8%3D

反射反射,程序员的快乐

  /// <summary>
        /// 注册引用程序域中所有有AppService标记的类的服务
        /// </summary>
        /// <param name="services"></param>
        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<AppServiceAttribute>();

                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/


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK