zoukankan      html  css  js  c++  java
  • 减少到处衍生的实体类

    这里的实体类更倾向于数据传输对象(既DTO)。无论是编码风格采用 事务脚本 还是 领域模型,我们都会遇到各种各样的数据传输对象,尤其是传统事务脚本三层架构的编码中,更会遇到各类实体对象,一般来说,这些实体对象产生的原因如下:

    1:为各类报表和查询服务的联表查询,会导致字段变多,带来的实体的属性增多。怎么办,创造新的实体类或者为原有实体类新增字段?

    2:前台不同的需求场合,会构建不同的 DTO 对象(如JSON对象)传递到后台,新的对象产生就创造新的实体类或者为原有实体类新增字段?

    我们大多会遇到以上这类问题。以下是避免创建新的实体类或者为原有实体类新增字段的一些措施。

    先来看第一种情况。

    一:联表查询中的字段增多

    这是从里(数据层)发散到外(UI层)的过程。

    以往我们像这样编码:

    public override IList<User> GetList()
    {
        string sql = "select * from [EL_Organization].[User] where state=1";
        var ds = SqlHelper.ExecuteDataset(
            SqlHelper.ConnectionString,
            CommandType.Text,
            sql);
        return DataTableHelper.ToList<User>(ds.Tables[0]);
    }

    现在,我们利用匿名类型:

    public IEnumerable GetList2()
    {
        string sql = "select * from [EL_Organization].[User] where state=1";
        var ds = SqlHelper.ExecuteDataset(
            SqlHelper.ConnectionString,
            CommandType.Text,
            sql);

        var oblist = new List<object>();
        foreach (DataRow row in ds.Tables[0].Rows)
        {
            oblist.Add(new { Id = row["Id"], Name = row["Name"] });
        }

        return oblist;
    }

    你可能注意到两点变化,

    第一:我们原先使用泛型+反射的方法(通用方法,不赘述),直接返回了 IList<User>,而现在,我们没有这样的通用方法了,只能通过一个 foreach 循环自己来构建这样的匿名类型及其列表。你可能会首先担心循环中的那些代码繁琐而机械,但是这一定比创建一个新类型来的简单。其次,你可能会想到改造原先的泛型+反射方法,使其支持匿名类型,但当我写完这个函数的时候,我有点担心其效率,所以我仍旧推荐上面的写法。

    第二点变化:返回类型是一个 IEnumerable 了,替代了原先的 IList<User>。这仿佛牺牲了一些原先作为强类型的特性,但这些特性是可克服的。有一种做法是,将返回类型修改为 IList<dynamic>,不过这仍旧带来一点点性能损耗,因此我仍旧建议,如无特殊必要(如:在业务层需要对返回结果进行赋值),返回 IEnumerable 已足够。

    现在,Dal 层已经是这样,上次调用者该如何用,以 MVC 中的控制器为例,我们应该如下使用(备注,下面这个方法针对是上层返回类型是一个 IEnumerable ,如果返回 List<dynamic>,则参考“ExpandoObject对象的JSON序列化” ):

    public JsonResult xTest()
    {
        UserDal dal = new UserDal();
        var list = dal.GetList2();
        return this.Json(list, JsonRequestBehavior.AllowGet);
    }

    你可能会觉得,这太简单,直接从 DAL 层取出来,就丢给前台了。没错,在事务脚本编码时,很多时候就是这么简单,即便仍旧要对 DAL 返回值做特殊处理,采用匿名类型也不会阻止我们什么事情。

    二:构建不同的对象(如JSON对象)传递到后台

    我们知道,MVC 中的控制器,如果参数中带了强类型的参数,则 MVC 引擎会自动前台 post 过来的 JSON 对象转换为该强类型。如,我们后台是这样的(即,在参数中声明了强类型):

    public class xTemp
    {
        public string XId { get; set; }
        public string XName { get; set; }
    }

    public JsonResult xTest(xTemp SomeData)
    {
        。。。
        return this.Json(easyUiPages, JsonRequestBehavior.AllowGet);
    }

    以及前台是这样的:

    var SomeData = {
        "xId": "xxx",
        "xName": "zzz"
    };
    $.ajax({
        type: "post",
        data: SomeData,
        url: "@ViewBag.Domain/home/xTest",
        success: function (data) {
            ;
        }
    });

    则我们的后台一定会解析得到这个对象。但是,如果我们将控制器中的 SomeData的声明换成了 object 或者 dynamic,则我们什么也不会得到。所以,如果不想创建新的类型,则有集中做法:

    1:一一获得对象的属性

    直接传递或再创建 dynamic 对象,然后将其传递到 bll 或者 dal 层进行处理;一般要处理的对象属性少于3个,可这样处理。

    2:或者,还是老老实实创建实体吧

    传统我们至少有三种选择:创建 ViewModel 或 领域实体 或 数据实体。

    2.1ViewModel 是指在 UI 层的实体,默认在 UI 层创建一个 ViewModel 的文件夹进行放置,这些实体不发散到其它层。当然,如果一定要发散到下层,可以传递 dynamic;

    2.2 领域实体,则指在业务逻辑层的实体,或者我们也称之为业务实体。业务实体和业务类并行放置一起,上层(如 UI 层)可访问,下层不可访问;

    2.3 数据实体,则指最底层的,连 DAL 层都能访问的实体,一般用于映射数据库表(联表则使用导航属性,如 EntityFramework 等使用的),但一些通用的实体类,也可放置在此层。备注:也可以将领域实体作为数据实体;

    在一些小型应用中,往往 ViewModel 、领域实体、数据实体,统一归并到实体层。总之,实体层的创建和放置必须非常明确,随意创建并且随意放置的实体类,会导致代码膨胀并给重构带来灾难。

    如果我们发现数据实体已经不够用,这里建议的路线图是:

    1:修改数据实体或创建新的数据实体;

    2:如果数据实体不能动,或不想动,则创建 ViewModel;

    3:如果发现业务类需要用到该实体,则把该 ViewModel 重构为 领域实体;

    4:如果发现该实体要传递到 DAL 层,那么我们有四种选择,

    4.1 手动转为数据实体;

    4.2 回到第一步,将实体重构为数据实体;

    4.3 使用 dynamic 传递到 Dal 层;

    4.4 将属性作为参数传递;

    创建多余的实体的做法,可以看到我们啰啰嗦嗦讲了这么多,如果你已经觉得很烦了,或者觉得自己根本掌握不了这个度,那么我们的终极做法是下面的这个做法。

    3:接受 JSON 字符串,反序列化为 dynamic

    上面我们说到:将控制器中的 SomeData的声明换成了 object 或者 dynamic,则我们什么也不会得到。但是,我们可以这么做,

    前台的 data: SomeData ,换为:

    data: "someData=" + JSON.stringify(SomeData)

    注意,因为我们这里实际传递的是字符串,所以不能声明:

    contentType: "application/json; charset=utf-8",

    后台则为:

    [SessionFilter]
    public JsonResult XTest(string someData)
    {
        var jsSerializer = new JavaScriptSerializer();
        var model = jsSerializer.Deserialize(somData, typeof(dynamic)) as dynamic;
        //如果是列表,则像如下处理
        //var models = jsSerializer.Deserialize(somData, typeof(List<dynamic>)) as List<dynamic>;

        省略
        return this.Json(result, JsonRequestBehavior.DenyGet);
    }

    这是建议的、减少实体类型的行之有效的方法。

    三:另一种思路:构建通用的实体类

    该思路的中心思想就是:消灭所有的实体类,任何传统的实体无非就是:类型名+属性名+属性值,于是,我们就构建这样的一个通用实体类,类似:

    [Serializable]
    public sealed class DynamicDalObject : DynamicObject, IDictionary<string, object>
    {
        public DynamicDalObject()
        {
            this._inner = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        }

        public DynamicDalObject(IEnumerable<KeyValuePair<string, object>> keyValuePairs) : this()
        {
            foreach (var keyValuePair in keyValuePairs)
            {
                this._inner.Add(keyValuePair);
            }
        }

        ……
    }

    然后,发散到 DAL 层,就可以进行这样的操作:

    public static int Update(bool isOnline, int viewdCount, string Id)
    {
        using (var manger = new DynamicDalSqlManager())
        {
            return
                manger.DataAccess.Update(
                    new DynamicDalObject(
                        new[]
                            {
                                new KeyValuePair<string, object>("id", Id),
                                new KeyValuePair<string, object>("IsOnline", isOnline),
                                new KeyValuePair<string, object>("ViewdCount", viewdCount),
                            }),
                    "EL_Course.CourseHistory",
                    new[] { "id" });
        }
    }

    优点:

    1:只要不想用实体类,就用它解决;

    2:可构建通用的 Dal,即,DAl 只要一个也就够了;

    缺点:

    1:失去了面向对象结构,实际上变成了面向 SQL 编码;

    2:不停的拆箱、装箱;

    这种通用实体类的做法,在一些业务相对单一,可尝试使用,能带来短平快的效果。

    四:总结

    总之,应充分利用 匿名类型 和 dynamic 类型来减少无必要的或者模棱两可的实体类的创建,最后,再明确一下建议的架构思路:

    1:首先我们有数据实体层,该实体层可拓展;

    2:若现有实体不能满足需要,则从 DAL 到 UI,可使用匿名类型;

    3:若现有实体不能满足需要,则从 客户端 到 控制器,可 JSON 字符串(非对象),在控制器中反序列化为 dynamic 类型;

    4:最后,如发现某个业务方法被多个场景使用,可再将其 dynamic 对象重构成一个 数据实体(优先) 或 领域实体。为什么数据实体优先呢,因为从业务层传递到 DAL 层,我们可以直接传递该强类型对象。

    5:在领域模型中,DTO 对象适用于本建议。

  • 相关阅读:
    2012年6月30号PMP考试总结
    安装了VS2010 sp1 后再安装ASP.NET MVC 3.0的问题(Final Result: Installation failed with error code: (0x80070643), "安装时发生严重错误 " (Ela)
    黄光裕方面独家点评国美中报:领先优势即将丧失
    Asp.net 模板页(MasterPage)里的 js脚本路径
    Windows 7 Sp1 x64 无法安装英文语言包(已解决)
    The issue of vertical lines throughing the Report footer or the whole page in Crystal report 11
    Windows Server 2008 R2 with Service Pack 1
    垃圾农业银行网银https://www.95599.cn/PersonalBank/startUpHtmlSessionAction.ebf
    如何填写PMP审计表单,Audit注意事项
    被日军飞机炸毁的上海南火车站铁轨上拍摄了一个父母刚被炸死、正在铁轨上深哀啼号的婴儿
  • 原文地址:https://www.cnblogs.com/luminji/p/3537793.html
Copyright © 2011-2022 走看看