程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 減少到處衍生的實體類

減少到處衍生的實體類

編輯:C#入門知識

這裡的實體類更傾向於數據傳輸對象(既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 對象適用於本建議。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved