表現層的設計(一)——常用的模式、JSON與DTO
上幾篇博文介紹了 業務邏輯層和數據訪問層,我認為寫博文的作用主要是向業界的讀者交流一種思想,點到為止,至於學習架構設計,通過幾篇博文是講不清楚的,還需要【基礎】扎實的情況下,【反復】研究【權威】的書籍。
你會發現我寫隨筆的特點就是喜歡單一,講NHibernate就絕不會把easyui參合進來,而這次要談得json也不會和MVC有什么關系。
而實戰當中,你會發現我確實可以將他們分開,在需要的時候重新組合各種類庫和框架來達到我的目的。
表現層(Presentation Layer)
它主要由兩部分組成:
1.界面UI
2.表現層邏輯
界面UI在.NET中包括的幾種形式:WEB(ASP.NET)、WPF、WinForm、Mobile
而表現層邏輯通常需要用后台代碼做一些事情。
理論上我們應該盡量解除兩者之間的依賴,以便於UI更容易切換。
關於表現層的一些誤區
1. 覺得拖控件比較低端
由於許多培訓學校在解說JAVA和.NET的區別的時候,通常會講一個能拖控件而一個不能,不能拖控件需要程序員自己寫更加高端。
而事實上“拖控件”有個學名的,叫 快速應用程序開發(Rapid Application Development,RAD)。
樓主從學生年代VB/Delphi/C++都是控件拖過來的,到現在也不會覺得它低端,它一種戰術。
實際上在傳統的ASP.NET中也可以使用MVP模式來做到分離關注點,高不高端,取決於寫程序的人,而不在於用什么工具。
就好像昔年兵器譜排名第三的小李飛刀,他的飛刀也只不過是大治的鐵匠花了三個時辰打造而成的。
2.關於json的誤區
很多人認為json只有在Web開發中有效,甚至認為Easyui+ASP.NET MVC3中間通過json傳輸數據是唯一的情況。
而事實上我們可以用它作為跨平台的傳輸格式,WCF正是利用了這一點。
所以對象序列化json的時候,可能ASP.NET MVC3並不是必須的。
設計類庫時應該避開對其他框架的依賴。
3.MVC就是ASP.NET MVC x
實際上MVC只是一個模式,而微軟對它進行了改進,設計出了ASP.NET MVC框架。
MVC也只不過是WEB應用程序才使用的一種模式,夠作為架構設計師,可能需要了解更多。
另外MVC還延伸出幾種模式,分別是MVP模式、PM模式。
表現層常用的模式
1.web中
使用傳統的ASP.NET,可以適當使用MVP模式分離關注點;
或者使用ASP.NET MVC框架;
2.windows中
使用winForm同樣是適當使用MVP模式;
使用WPF可以考慮MVVM模式;
3.移植的考慮
如果你希望你的winForm程序可以移植到Linux換成GTK的話,MVP模式也許是唯一的選擇了。
json與DTO的想法
關於Json:ASP.NET MVC框架只能用於WEB應用程序,而json則可以更廣泛地應用,所以json序列化的類庫與框架的依賴並不是很好的設計。
在winform的項目中,這樣的類庫拿過來顯然就編譯不能通過了。
關於DTO:DTO,數據傳輸對象。理論上在表示層得到數據時應該是DTO,在服務層已經將DataTable/ORM的Model轉化成DTO。但實際上這是一個很大的工作量,實際上可能直接使用ORM的Model傳輸給表現層。
所以我們設計Json序列化類的時候需要考慮幾個問題:
1.我們接受的要序列化對象可能是DTO,也可能是ORM的領域模型,而領域模型經常會出現循環依賴,延遲加載。直接用微軟自帶的序列化類庫報錯,而使用MVC框架來指定要序列化的屬性那么如果不能使用MVC框架又該怎么辦?
2.服務器向客戶端傳輸數據的性能考慮,則不應該傳輸多余的數據。Json序列化時該如何排除不需要的屬性?
3.如果是老系統使用.NET,沒有微軟自帶的Json序列化類庫,使用第三方類庫。
給出簡單的解決方案
這里使用了Newtonsoft.Json,並對前人類庫進行一些整理,忽略對象循環依賴,能夠支持移除不需要的屬性。
序列化與反序列化
[Test]
public void XLH()
{
DictionaryRepository rep = new DictionaryRepository();
Dictionary dic = rep.GetByCodeLazy("bianma1", "code1");
string json = Util.Json.Json.Serializer(new { dic ,total=3}, new string[] { "Category" });
Console.WriteLine(json);
}
[Test]
public void FXLH()
{
string json="{\"Index\":1.0,\"Description\":\"描述1\",\"Id\":\"160954d1-5e73-4ac8-a426-197f5bd616f9\",\"Name\":\"字段1\",\"Code\":\"code1\"}";
Dictionary dc=Util.Json.Json.DeSerializer<Dictionary>(json, null);
}
序列化與反序列化
下面兩個類可以直接使用。
類庫1
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using System.IO;
namespace Util.Json
{
public class Json
{
/// <summary>
/// 序列化
/// </summary>
/// <param name="obj"></param>
/// <param name="settings"></param>
/// <returns></returns>
public static string Serializer(object obj, JsonSerializerSettings settings)
{
JsonSerializer scriptSerializer = JsonSerializer.Create(settings);
StringWriter sw = new StringWriter();
scriptSerializer.Serialize(sw, obj);
string str = sw.ToString();
sw.Close();
return str;
}
/// <summary>
/// 序列化(忽略對象循環依賴,忽略空值)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string Serializer(object obj)
{
JsonSerializerSettings Settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
DateTimeZoneHandling = DateTimeZoneHandling.Local
};
return Serializer(obj, Settings);
}
/// <summary>
/// 序列化(忽略對象循環依賴,忽略空值,排除指定列)
/// </summary>
/// <param name="obj">序列化目標對象</param>
/// <param name="lstExclude">要排除的屬性名列表</param>
/// <returns></returns>
public static string Serializer(object obj,string[] lstExclude)
{
ExcludePropertiesContractResolver exclude = new ExcludePropertiesContractResolver(lstExclude);
JsonSerializerSettings Settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
DateTimeZoneHandling = DateTimeZoneHandling.Local,
ContractResolver = exclude
};
return Serializer(obj, Settings);
}
/// <summary>
/// 反序列化
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param>
/// <param name="Settings"></param>
/// <returns></returns>
public static T DeSerializer<T>(string str, JsonSerializerSettings Settings)
{
JsonSerializer scriptSerializer = JsonSerializer.Create(Settings);
JsonTextReader sr = new JsonTextReader(new StringReader(str));
T obj = scriptSerializer.Deserialize<T>(sr);
sr.Close();
return obj;
}
}
}
類庫1
類庫2
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
namespace Util.Json
{
/// <summary>
/// 重寫創建屬性列表函數,使之支持排除指定屬性
/// </summary>
public class ExcludePropertiesContractResolver : DefaultContractResolver
{
string[] lstExclude;
public ExcludePropertiesContractResolver(string[] excludedProperties)
{
lstExclude = excludedProperties;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> list = base.CreateProperties(type, memberSerialization);
foreach (string item in lstExclude)
{
JsonProperty temp = null;
foreach (JsonProperty jp in list)
{
if (jp.PropertyName == item)
{
temp = jp;
}
}
if (temp != null)
{
list.Remove(temp);
}
}
return list;
}
}
}
類庫2
