zoukankan      html  css  js  c++  java
  • 提高生产性工具(五)

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

    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);
            }


  • 相关阅读:
    volley框架使用
    Insert Interval
    candy(贪心)
    Best Time to Buy and Sell Stock
    Best Time to Buy and Sell Stock III
    distinct subsequences
    edit distance(编辑距离,两个字符串之间相似性的问题)
    trapping rain water
    word break II(单词切分)
    sudoku solver(数独)
  • 原文地址:https://www.cnblogs.com/TextEditor/p/4402772.html
Copyright © 2011-2022 走看看