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

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 = ,
MultiMasterTable = ,
SingleEnum = ,
MultiEnum = ,
Datetime = ,
Boolean = ,
SingleMasterTableWithGrade =,
MultiMasterTableWithGrade = ,
SingleCatalogMasterTable = ,
MultiCatalogMasterTable =
}
/// <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 = ; 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 > )
{
FilterItemQuery = Query.And(FilterItemQuery, filterItemList.GetQuery());
}
break;
case "FilterItemWithGradeList":
FilterItemWithGradeList filterItemWithGradeList = (FilterItemWithGradeList)item;
if (filterItemWithGradeList.Itemlist.Count > )
{
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);
}


提高生产性工具(五) - 数据的过滤器和图形化(适用于 MVC5 + MongoDB)的更多相关文章

  1. 提高生产性工具 - Model代码生成器(NET / JAVA) (一)

    原来在上一家公司,整整一年都在做工具,提高生产性,那个项目特别巨大,所以总共为老东家节约了500K左右的美金. (除了表扬之外,我个人什么好处都没有,领导们都升官发财了,郁闷) 到了新公司,也准备开发 ...

  2. 如何使用IDEA开发工具中右键中的Git图形化工具

    首先,你的项目一定是git服务器上面down下来的,下面来演示如何使用IntelliJ IDEA 开发中在鼠标右键中提供的一个非常方便的图形化Git管理工具: 这里使用的IDEA开发工具的版本是 In ...

  3. 提高生产性工具 - Model代码生成器(二)

    首先,我想阐述一下我开发这个工具的一个观点. 如果大家做过对日软件的话,很多时候,日方是进行设计的,数据模型是什么样子的,各个字段的类型,需要做什么验证,验证规则,错误信息,都是日方制定的. 外包就是 ...

  4. 提高生产性工具(四) - XML数据库的尝试

    首先祝大家新年快乐.身体健康,平安就是福气. 对于一般的个人迷你项目,数据量不大的时候,完全没有必要使用数据库,管理数据使用XML就可以了. 自己尝试写了一个XML数据库,插入1w条小记录,大概3M大 ...

  5. 6.MySQL图形化工具的使用

    6.图形化工具的使用6.1 Mysql Workbench Mysql Workbench是Mysql官方推出的集成图形化工具,替代了之前的图形化管理工具Mysql Administrator和图形化 ...

  6. 图形化unix/linux 工具 mobarxterm

    1.使用  mobarxterm 图形化登录工具 2. 如果服务器是图形化界面启动的,xhost +命令可以不用执行 [root@test ~]# xhost +xhost:  unable to o ...

  7. 入门大数据---Hbase 过滤器详解

    一.HBase过滤器简介 Hbase 提供了种类丰富的过滤器(filter)来提高数据处理的效率,用户可以通过内置或自定义的过滤器来对数据进行过滤,所有的过滤器都在服务端生效,即谓词下推(predic ...

  8. 微信小程序开发工具的数据,配置,日志等目录在哪儿? 怎么找?

    原文地址:http://www.wxapp-union.com/portal.php?mod=view&aid=359 本文由本站halfyawn原创:感谢原创者:如有疑问,请在评论内回复   ...

  9. [.net 面向对象程序设计进阶] (26) 团队开发利器(五)分布式版本控制系统Git——图形化Git客户端工具TortoiseGit

    [.net 面向对象程序设计进阶] (26) 团队开发利器(五)分布式版本控制系统Git——图形化Git客户端工具TortoiseGit 读前必备: 接上篇: 分布式版本控制系统Git——使用GitS ...

随机推荐

  1. PHP面向对象07_PDO

    oop007复习 2014-9-4 9:42:28 摘要: 1.pdo的作用 2.pdo的安装 3.pdo连接属性设置 4.pdo对象和PDOStatement对象 5.pdo预处理 6.pdo事务机 ...

  2. Atitit 发帖机系列(7) 词法分析的方法attilax大总结)

    Atitit 发帖机系列(7) 词法分析的方法attilax大总结) 1.1. 词法分析貌似俩大方法,一个直接根据状态图转换,一个根据dfa1 1.2. switchcase或者ifelse 最原始方 ...

  3. redis基本配置和相关设置

    redis-cli:the redis command line interface command line usage: $redis-cli incr mycounter 输出的结果只会显示在终 ...

  4. fir.im Weekly - 可能是 iOS 审核最全面的解决方案

    ipv6 被拒绝,后台定位被拒绝--让很多国内 iOS 开发者心力交瘁.这是一份关于 iOS 审核的终极免费方案,作者iOSWang对最近iOS 审核被拒问题给出了比较全面的方案:Solve-App- ...

  5. salesforce 零基础开发入门学习(九)Approval Process 介绍

    在阅读此篇文章前,可以先参考阅读一个前辈总结的关于Approval Process的操作.以下为参考的链接: http://www.cnblogs.com/mingmingruyuedlut/p/37 ...

  6. iOS-ARC

    1. 本文的主要内容: ARC的本质 ARC的开启与关闭 ARC的修饰符 ARC与Block ARC与Toll-Free Bridging ARC的本质 ARC是编译器(时)特性,而不是运行时特性,更 ...

  7. DOM_03之元素及常用对象

    1.修改样式:访问内联样式:elem.style.css属性名:获得其他:var style=getComputerStyle(elem):*(* 获得焦点onfocus:失去焦点onblur:): ...

  8. EFProf Entity Framework Profile 工具

    SQL Server Profiler用来跟踪应用程序发送到SQL Server中的SQL语句,用于检测性能,查找问题.Entity Framework 也有它的跟踪工具EFProf,用于跟踪Enti ...

  9. hibernate(八)一对多关联

    一.一对多单向关良 一对多单向关联与多对一相似 假设一个组有多个用户,即一(Group)对多(User) 需要在Group类中添加一个User类的Set集合(数据库中的用户不可能是重复的,所以要用Se ...

  10. poj1062昂贵的聘礼(Dijkstra**)

    /* 题意: 物主有一个物品,价值为P,地位为L, 以及一系列的替代品Ti和该替代品所对应的"优惠"Vi g[u][i] 表示的是u物品被i物品替换后的优惠价格!(u>0, ...