• 提高生产性工具(五)


    在下面流水账似的文章之前,先将一些感悟说一下。

    1.如果一个系统对于某个功能在至少三个地方使用的话,必须将其抽象提炼出来,而且时间点最好是大规模测试之前。

    2.提炼出来的功能,如果品质做得好,整个系统的品质也提高不少。

    3.提炼出一个泛用的功能需要高度的技巧很时间,但是对于后期维护,应对需求变更是非常有好处的,甚至可以通过仅仅修改模型的特性就完成需求变更。

    4.任何一个三年以上的CSharper都可以写一个性能和品质不错的功能,但是要将一个功能做到泛用,具有高度的可扩展性,需要10年左右的经验,大龄程序员应该多关注业务,多思考如何做个泛用功能,不是被多变的需求牵着鼻子走。泛用的东西,先苦后甜。

    5.设计模式不是背书,在于灵活应用,设计模式不是死的,有时候,有些需求需要模式之间的组合,以及一些模式的变体。

    关于MongoDB

    MongoDB的好处是

    你可以摆脱固定数据表字段的约束。

    原生天然支持ORM(阶层结构的数据库,就是为了OOP对象而生的)。

    某些方面拥有比SQL更灵活的Query。

    MongoDB的坏处是

    如果你删除一个字段,或者修改一个字段,在序列化的时候就会出错(系统发现一个在新的实体中不存在的属性。。)。而且不想Relation那样,可以通过一个SQL语句就改变表结果,后期维护会有一些额外成本。

    以下内容可以忽略:

    随着大量数据的采集,数据的过滤和可视化将越来越多的出现在实际需求中.

    如何制作一个泛用的数据过滤器和可视化设定器,则是一个新的课题.

    这里数据库使用的是MongoDB,由于MongoDB在Query上的一些特点,数据过滤变得十分简单了.

    假设我们有一张名为人才储备的表格,上面有很多字段,哪些字段是可以用来做过滤的,过滤器的类型是什么,我们在写实体代码的时候,其实可以使用 Attribute 进行标注,然后在运行时,通过读取这些标记,就可以获得可用的过滤器列表.

    using System;
    
    namespace BussinessLogic.Entity
    {
        [AttributeUsage(AttributeTargets.Property)]
        public class FilterItemAttribute : Attribute
        {
            /// <summary>
            /// 数据形式枚举
            /// </summary>
            public enum StructType
            {
                SingleMasterTable = 0,
                MultiMasterTable = 5,
                SingleEnum = 10,
                MultiEnum = 15,
                Datetime = 20,
                Boolean = 25,
                SingleMasterTableWithGrade =30,
                MultiMasterTableWithGrade = 35,
                SingleCatalogMasterTable = 40,
                MultiCatalogMasterTable = 45
            }
            /// <summary>
            /// 数据形式
            /// </summary>
            public StructType MetaStructType { get; set; }
            /// <summary>
            /// 元数据类型
            /// </summary>
            public Type MetaType { get; set; }
        }
    }

    如果某个字段是可以多选的,可选值是从MasterTable来的,它就是MultiMasterTable,单选的,则是Single。如果可选值是来自于枚举的,则MasterTable变为Enum。

    下面将这些特性作用于具体的实体类

    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using MongoDB.Bson.Serialization.Attributes;
    using System;
    
    namespace BussinessLogic.Entity
    {
        /// <summary>
        ///     人才储备
        /// </summary>
        public partial class TalentInfo : CompanyTable
        {
            #region "model"
    
            /// <summary>
            ///     姓名
            /// </summary>
            [DisplayName("姓名")]
            [Required]
            public string Name { get; set; }
    
            /// <summary>
            ///     英语名
            /// </summary>
            [DisplayName("英语名")]
            public string EnglishName { get; set; }
    
            /// <summary>
            ///     生日
            /// </summary>
            [DisplayName("生日")]
            [Required]
            [DataType(DataType.Date)]
            [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
            [BsonDateTimeOptions(Kind = DateTimeKind.Local)]
            public DateTime BirthDay { get; set; }
    
            /// <summary>
            ///     出生地
            /// </summary>
            [DisplayName("出生地")]
            public string BornIn { get; set; }
    
            /// <summary>
            ///     常住地
            /// </summary>
            [DisplayName("常住地")]
            [Required]
            public string Location { get; set; }
    
            /// <summary>
            ///     手机
            /// </summary>
            [DisplayName("手机")]
            [Required]
            [DataType(DataType.PhoneNumber)]
            [RegularExpression(@"^1[3458][0-9]{9}$", ErrorMessage = "手机号格式不正确")]
            public string Mobile { get; set; }
    
            /// <summary>
            ///     电子邮件
            /// </summary>
            [DisplayName("电子邮件")]
            [Required]
            [DataType(DataType.EmailAddress)]
            public string Email { get; set; }
    
            /// <summary>
            ///     大学
            /// </summary>
            [DisplayName("大学")]
            [Required]
            public string University { get; set; }
    
            /// <summary>
            ///     专业
            /// </summary>
            [DisplayName("专业")]
            [Required]
            public string Major { get; set; }
    
            /// <summary>
            ///     学位
            /// </summary>
            [DisplayName("学位")]
            [UIHint("Enum")]
            public AcademicType Academic { get; set; }
    
            /// <summary>
            /// 海外工作背景
            /// </summary>
            [DisplayName("海外工作背景")]
            [FilterItem(MetaStructType = FilterItemAttribute.StructType.Boolean)]
            public bool OverseaWork { get; set; }
    
            /// <summary>
            /// 海外教育背景
            /// </summary>
            [DisplayName("海外教育背景")]
            [FilterItem(MetaStructType = FilterItemAttribute.StructType.Boolean)]
            public bool OverseaEdu { get; set; }
    
            /// <summary>
            ///     行业背景
            /// </summary>
            [DisplayName("行业背景")]
            public List<string> IndustryBackgroundList { get; set; }
    
            /// <summary>
            ///     上一家公司
            /// </summary>
            [DisplayName("上一家公司")]
            public string PreEmp { get; set; }
    
            /// <summary>
            ///     上一家公司行业
            /// </summary>
            [DisplayName("上一家公司行业")]
            public string PerInd { get; set; }
            /// <summary>
            ///     招聘渠道
            /// </summary>
            [DisplayName("招聘渠道")]
            public string Channel { get; set; }
    
            /// <summary>
            ///     语言
            /// </summary>
            [DisplayName("语言")]
            [FilterItem(MetaStructType = FilterItemAttribute.StructType.MultiMasterTableWithGrade, MetaType = typeof(M_Language))]
            public List<ItemWithGrade> LanguageList { get; set; }
    
            /// <summary>
            ///     技能
            /// </summary>
            [DisplayName("技能")]
            [FilterItem(MetaStructType = FilterItemAttribute.StructType.MultiCatalogMasterTable , MetaType = typeof(M_Skill))]
            public List<string> SkillList { get; set; }
    
            /// <summary>
            ///     等级
            /// </summary>
            [DisplayName("等级")]
            [UIHint("Enum")]
            [Required]
            public CommonGrade TalentRank { get; set; }
    
            /// <summary>
            ///     评价
            /// </summary>
            [DisplayName("评价")]
            public string Evaluate { get; set; }
    
            /// <summary>
            ///     评价
            /// </summary>
            [DisplayName("备注")]
            public string Comment { get; set; }
    
    
            /// <summary>
            ///     数据集名称
            /// </summary>
            public override string GetCollectionName()
            {
                return "TalentInfo";
            }
    
            /// <summary>
            ///     数据集名称静态字段
            /// </summary>
            public static string CollectionName = "TalentInfo";
    
    
            /// <summary>
            ///     数据主键前缀
            /// </summary>
            public override string GetPrefix()
            {
                return string.Empty;
            }
    
            /// <summary>
            ///     数据主键前缀静态字段
            /// </summary>
            public static string Prefix = string.Empty;
    
            /// <summary>
            ///     Mvc画面的标题
            /// </summary>
            [BsonIgnore] public static string MvcTitle = "人才储备";
    
            #endregion
        }
    }

     系统将自动通过反射机制获得FilterItemAttribute特性,根据特性来生成过滤器项目(filterItem)。

      由于是动态的,所以,很容易的增加和修改过滤器项目(filterItem)。

      过滤器项目(filterItem)也分多种类型,例如一个布尔型过滤器项目,则负责诸如 “过滤出全部具有 海外工作经验 的人”这样的任务。

     

    using MongoDB.Driver;
    using MongoDB.Driver.Builders;
    
    namespace BussinessLogic.Entity
    {
        /// <summary>
        /// 布尔型的过滤器
        /// </summary>
        public class FilterItemBoolean : FilterItemBase
        {
            /// <summary>
            /// 是否
            /// </summary>
            public bool YesOrNo;
            public FilterItemBoolean(string _FieldName)
            {
                FieldName = _FieldName;
                IsActive = false;
            }
            /// <summary>
            /// 获得Query
            /// </summary>
            /// <returns></returns>
            public override IMongoQuery GetQuery()
            {
                return Query.EQ(FieldName, YesOrNo);
            }
        }
    }

    每个过滤器,一旦属性设定完成,MongoDB的查询条件则也可以自动获得了。例如上面代码的 Query.EQ(FieldName, YesOrNo)

    表示一个查询条件 :记录的名为 FieldName的元素的值为 YesOrNo(EQ表示等于)。

    多个子条件可以组合成一个真正的过滤器(FilterSet)。当然,由于过滤器项目的过滤条件是自动获得的,所以整个过滤器的过滤条件也是很容易直接获得的。

    using MongoDB.Bson.Serialization.Attributes;
    using MongoDB.Driver;
    using System.ComponentModel;
    using MongoDB.Driver.Builders;
    using System;
    using System.Collections.Generic;
    using InfraStructure.DataBase;
    
    namespace BussinessLogic.Entity
    {
        /// <summary>
        ///     过滤器中心
        /// </summary>
        public class FilterSetCenter : FilterSetBase
        {
            #region "model"
    
            /// <summary>
            ///     数据集名称
            /// </summary>
            public override string GetCollectionName()
            {
                return "FilterSetCenter";
            }
    
            /// <summary>
            ///     数据集名称静态字段
            /// </summary>
            public static string CollectionName = "FilterSetCenter";
    
    
            /// <summary>
            ///     数据主键前缀
            /// </summary>
            public override string GetPrefix()
            {
                return string.Empty;
            }
    
            /// <summary>
            ///     数据主键前缀静态字段
            /// </summary>
            public static string Prefix = string.Empty;
    
            /// <summary>
            ///     Mvc画面的标题
            /// </summary>
            [BsonIgnore]
            public static string MvcTitle = "过滤器中心";
    
            /// <summary>
            /// 设定DisplayName
            /// </summary>
            public void SetDisplayName()
            {
                // 考虑到DisplayName和FieldName的关联性
                // 以及DisplayName可能会修改名称
                var type = Type.GetType(ModelName);
                for (int i = 0; i < FilterItems.Count; i++)
                {
                    FilterItems[i].DisplayName = EasyQuery.GetDisplayName(FilterItems[i].FieldName, type);
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="ModelName"></param>
            /// <param name="CompanyId"></param>
            /// <param name="AccountCode"></param>
            /// <returns></returns>
            public static List<MasterWrapper> GetFilterWrapperList<T>(string CompanyId, string AccountCode)
            {
                var FilterMaster = new List<MasterWrapper>();
                var CompanyIdQuery = EasyQuery.CompanyIdQuery(CompanyId);
                var AccountIdQuery = EasyQuery.AccountCodeQuery(AccountCode);
                var ModelNameQuery = Query.EQ("ModelName", typeof(T).FullName);
                var FilterQuery = Query.And(CompanyIdQuery, AccountIdQuery, ModelNameQuery);
                var FilterSetList = InfraStructure.DataBase.Repository.GetRecList<FilterSetCenter>(FilterSetCenter.CollectionName, FilterQuery);
                foreach (var filter in FilterSetList)
                {
                    FilterMaster.Add(new MasterWrapper
                    {
                        Code = filter.Code,
                        Rank = int.Parse(filter.Code),
                        Name = filter.Name,
                        Description = filter.Description
                    });
                }
                return FilterMaster;
            }
    
            /// <summary>
            /// 获得查询
            /// </summary>
            /// <returns></returns>
            public IMongoQuery GetQuery()
            {
                IMongoQuery FilterItemQuery = EasyQuery.CompanyIdQuery(CompanyId);
                foreach (var item in FilterItems)
                {
                    if (item.IsActive)
                    {
                        switch (item.GetType().Name)
                        {
                            case "FilterItemList":
                                FilterItemList filterItemList = (FilterItemList)item;
                                if (filterItemList.Itemlist.Count > 0)
                                {
                                    FilterItemQuery = Query.And(FilterItemQuery, filterItemList.GetQuery());
                                }
                                break;
                            case "FilterItemWithGradeList":
                                FilterItemWithGradeList filterItemWithGradeList = (FilterItemWithGradeList)item;
                                if (filterItemWithGradeList.Itemlist.Count > 0)
                                {
                                    FilterItemQuery = Query.And(FilterItemQuery, filterItemWithGradeList.GetQuery());
                                }
                                break;
                            default:
                                FilterItemQuery = Query.And(FilterItemQuery, item.GetQuery());
                                break;
                        }
    
                    }
                }
                return FilterItemQuery;
            }
            #endregion
        }
    }
    GetQuery()方法负责通过 MongoDB的 Query.And 方法将子条件组合在一起。当然,现在默认所有过滤器之间是And连接,稍加修改之后也可以用Or连接。
    过滤器配合UI之后,可以动态的生成如下的HTML画面。完全不需要任何多余的代码。



    下面这样一段代码就可以构成任意的过滤器编辑界面了。
    当然这里需要大约 500行的UI自动生成代码的支持。
    @model FilterSetCenter
    @{
        ViewBag.Title = "";
        if (Model.Code == ConstHelper.NewRecordCode)
        {
            ViewBag.Title = "创建-" + FilterSetCenter.MvcTitle;
        }
        else
        {
            ViewBag.Title = "编辑-" + FilterSetCenter.MvcTitle;
        }
        Layout = "~/Views/Shared/_DashBoardForMin.cshtml";
    }
    @using (Html.BeginForm())
    {
        <div class="form-horizontal">
            <div class="form-group">
                @Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
                </div>
            </div>
            <div class="form-group">
                @Html.LabelFor(model => model.Description, new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Description, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
                </div>
            </div>
            @foreach (FilterItemBase item in Model.FilterItems)
            {
                <div class="form-group">
                    @Html.Label(item.DisplayName, new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @(FilterHelper.GetFilter(item, ViewBag.CompanyId))
                    </div>
                </div>
            }
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="保存" class="btn btn-success" />
                    @Html.ActionLink("返回列表", "Index", null, new { @class = "btn btn-default" })
                </div>
            </div>
        </div>
    }

    UI辅助代码基本上完成的是类似于 html.EditFor这样的功能,也就是对于HTML代码的一个封装。例如,下面的代码生成一个面板。上面画面例子中,技能的面板也就是这样生成的。当然,整个画面由多个这样的UI辅助代码的共同协作才可以完成。一个方法错了,整个画面就可能出问题。但是如果可以控制每个方法的品质,则使用这些方法堆砌出来的界面的品质也是有保证的。

            /// <summary>
            /// 获得一个面板
            /// </summary>
            /// <param name="id"></param>
            /// <param name="strTitle"></param>
            /// <param name="strContent"></param>
            /// <returns></returns>
            public static MvcHtmlString GetPanel(string id, string strTitle, string strContent)
            {
                var html = "<div class="panel panel-info">" + Environment.NewLine;
                html += "<div class="panel-heading">" + Environment.NewLine;
                html += "<h3 class="panel-title">" + Environment.NewLine;
                html += "<a data-toggle="collapse" data-parent="#accordion" href="#collapse" + id + "">" + Environment.NewLine;
                html += strTitle  + Environment.NewLine;
                html += "</a>"  +Environment.NewLine;
                html += "</h3>" + Environment.NewLine;
                html += "</div>" + Environment.NewLine;
                html += "<div id="collapse" + id + "" class="panel-collapse collapse out">" + Environment.NewLine;
                html += strContent + Environment.NewLine;
                html += "</div>" + Environment.NewLine;
                html += "</div>" + Environment.NewLine;
                return new MvcHtmlString(html);
            }


  • 相关阅读:
    Dynamics CRM安装教程一:域环境准备
    C#判断日期是否合法
    Visual Studio 2015 无法加载.Net FrameWork4.6.2
    Dynamics CRM产生公共签名,避免每次插件换环境重新输入签名密钥账号密码
    Dynamics CRM使用JS隐藏自定义按钮
    <3>Python开发——列表(list)
    <2>Python开发——字符串(str)
    <1>Python开发——基础入门
    Sed命令详解
    国内搭建Minikube测试环境
  • 原文地址:https://www.cnblogs.com/TextEditor/p/4402772.html
Copyright © 2020-2023  润新知