【C#|.NET】從細(xì)節(jié)出發(fā)(一) 通用接口 aop dto 相關(guān)
2013-03-25 16:14 熬夜的蟲子 閱讀(4549) 評論(2) 收藏 舉報系列文章完成后 源碼發(fā)布在我的GIT上 https://github.com/dubing/
文章僅代表個人觀點(diǎn) 旨在交流 歡迎討論
背景
隨著信息化的普及,信息系統(tǒng)越來越多,通常不同系統(tǒng)是采用不同的技術(shù)基于不同平臺開發(fā)的,缺乏統(tǒng)一規(guī)劃、統(tǒng)一數(shù)據(jù)標(biāo)準(zhǔn)、統(tǒng)一調(diào)用接口,因此系統(tǒng)之間的交互變得很困難.通常大家在需要一個現(xiàn)有系統(tǒng)提供某方面功能的話就會讓開發(fā)人員提供個接口,webservice接口也好,標(biāo)準(zhǔn)http接口也好。然后需求不停的變更,代碼不停的迭代。隨著應(yīng)用端量的增多,對于類似業(yè)務(wù)邏輯提供的數(shù)據(jù)格式,內(nèi)容的特殊處理,dto的設(shè)計等等都在變化。用.Net的同學(xué)會了解多一些,用webservice的話,應(yīng)用端會生成代理類。服務(wù)端每次更新對應(yīng)應(yīng)用端都要同步更新。至此,我們引出第一個細(xì)節(jié)問題,解決服務(wù)端與應(yīng)用端的強(qiáng)依賴,設(shè)計通用的接口規(guī)范。
技術(shù)點(diǎn)
1. AOP(切面)
2. JavaScriptSerializer (json序列化)
3. DTO設(shè)計
實(shí)現(xiàn)
先設(shè)計一個通用的接口類
/// <summary>
/// 對象處理返回的結(jié)果接口
/// </summary>
/// <remarks>
/// 建議在代碼調(diào)用返回值中都采用此類實(shí)例為返回值<br />
/// 一般ResultNo小于0表示異常,0表示成功,大于0表示其它一般提示信息
/// </remarks>
public interface IAOPResult
{
/// <summary>
/// 返回代碼
/// </summary>
int ResultNo { get; }
/// <summary>
/// 對應(yīng)的描述信息
/// </summary>
string ResultDescription { get; }
/// <summary>
/// 相應(yīng)的附加信息
/// </summary>
object ResultAttachObject { get; }
/// <summary>
/// 內(nèi)部AOPResult
/// </summary>
IAOPResult InnerAOPResult { get; }
/// <summary>
/// 處理結(jié)果是否成功(ResultNo == 0)
/// </summary>
bool IsSuccess { get; }
/// <summary>
/// 處理結(jié)果是否失敗(ResultNo != 0 )
/// </summary>
bool IsNotSuccess { get; }
/// <summary>
/// 處理結(jié)果是否失敗(ResultNo < 0 )
/// </summary>
bool IsFailed { get; }
/// <summary>
/// 已處理,但有不致命的錯誤(ResultNo > 0)
/// </summary>
bool IsPassedButFailed { get; }
/// <summary>
/// 如果處理失敗,則拋出異常
/// </summary>
/// <returns>返回本身</returns>
IAOPResult ThrowErrorOnFailed();
}
設(shè)計一個里AOPResult實(shí)現(xiàn)這個接口,類中添加Serializable屬性表明可序列化,應(yīng)用到我們的具體的demo如下
[WebMethod(Description = "檢查購物車中的禮品")]
[SoapHeader("header")]
public AOPResult CheckGiftForCart(List<CartLineDTO> carlist, bool returnflag)
{
ValidateAuthentication();
return (AOPResult)CartGiftCenter.GiftService.CheckGiftForCart(carlist, returnflag);
}
[WebMethod(Description = "獲取所有的贈品信息")]
[SoapHeader("header")]
public AOPResult GetGiftList()
{
ValidateAuthentication();
var result = (AOPResult)CartGiftCenter.GiftService.GetModelList();
if(result.IsSuccess)
{
result.ResultAttachObject = result.ResultAttachObject.ObjectToJson();
}
return result;
}
[WebMethod(Description = "根據(jù)Id獲得組信息")]
[SoapHeader("header")]
public AOPResult GetGroupInfoByID(int Id)
{
ValidateAuthentication();
var result = (AOPResult)CartGiftCenter.GroupService.GetModelInfoById(Id);
if (result.IsSuccess)
{
var resultobj = result.ResultAttachObject as GiftGroup;
result.ResultAttachObject = new GroupLineDTO
{
GroupId = (int)resultobj.GroupId,
}.ObjectToJson();
}
return result;
}
從上面的例子我們可以看出所有的服務(wù)方法都采用了相同的返回類型。AOPResult中可以存對象,可以存數(shù)組,具體由自己定義。如果返回的是對象我們需要將他序列化成json方式。序列化方法參照下面
public static class JsonExtensions
{
private static JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
[DebuggerStepThrough]
public static string ObjectToJson(this object obj)
{
return javaScriptSerializer.Serialize(obj);
}
[DebuggerStepThrough]
public static T JsonToObject<T>(this string json)
{
if (string.IsNullOrEmpty(json)) return default(T);
return javaScriptSerializer.Deserialize<T>(json);
}
}
這么做的優(yōu)缺點(diǎn)是什么,優(yōu)點(diǎn)是服務(wù)協(xié)議的雙方有了共同的標(biāo)準(zhǔn),應(yīng)用端不再完全依賴于代理,也就是說服務(wù)端的變更不會直接影響到應(yīng)用端。舉個最簡單的例子,應(yīng)用a先向服務(wù)端索取一個對象A,A中包含c屬性,后來b也想服務(wù)端索取對象A,但是需要包含d屬性。服務(wù)器端更新重新build。按照普通設(shè)計a變也需要重新build引用新的服務(wù)代理。那么按照這個設(shè)計a便只需要從A中提取c就可以,不管服務(wù)端以后如何變更只要c屬性還在,結(jié)構(gòu)為改變就不需要重新build。
應(yīng)用端簡單示例
var b = CartGiftCenter.GiftService.GetModelList();
string message =b.ResultDescription;
var gift = b.ResultAttachObject as List<GiftDetail>;
另一外的主要的有點(diǎn)就是可以統(tǒng)一的切面編程,aop的概念這里就不多說了。利用aop的特點(diǎn)我們可以對服務(wù)的反饋?zhàn)鹘y(tǒng)一的操作、檢查。例如系統(tǒng)認(rèn)證、內(nèi)容過濾等等。這里推薦一篇簡單務(wù)實(shí)的文章 http://blog.csdn.net/yanghua_kobe/article/details/6917228
上文中所附的簡單切面輔助類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;
/// <summary>
///AspectF 的摘要說明
/// </summary>
public class AspectF
{
internal Action<Action> Chain = null;
internal Delegate WorkDelegate;
[DebuggerStepThrough]
public void Do(Action work)
{
if (this.Chain==null)
{
work();
}
else
{
this.Chain(work);
}
}
[DebuggerStepThrough]
public TReturnType Return<TReturnType>(Func<TReturnType> work)
{
this.WorkDelegate = work;
if (this.Chain==null)
{
return work();
}
else
{
TReturnType returnValue = default(TReturnType);
this.Chain(() =>
{
Func<TReturnType> workDelegate = WorkDelegate as Func<TReturnType>;
returnValue = workDelegate();
});
return returnValue;
}
}
public static AspectF Define
{
[DebuggerStepThrough]
get
{
return new AspectF();
}
}
[DebuggerStepThrough]
public AspectF Combine(Action<Action> newAspectDelegate)
{
if (this.Chain==null)
{
this.Chain = newAspectDelegate;
}
else
{
Action<Action> existingChain = this.Chain;
Action<Action> callAnother = (work) =>
existingChain(() => newAspectDelegate(work));
this.Chain = callAnother;
}
return this;
}
public static void Retry(int retryDuration, int retryCount, Action<Exception> errorHandler, Action retryFaild, Action work)
{
do
{
try
{
work();
}
catch (Exception ex)
{
errorHandler(ex);
System.Threading.Thread.Sleep(retryDuration);
throw;
}
} while (retryCount-- > 0);
}
}
public static class AspectExtensions
{
[DebuggerStepThrough]
public static void DoNothing()
{
}
[DebuggerStepThrough]
public static void DoNothing(params object[] whatever)
{
}
[DebuggerStepThrough]
public static AspectF Delay(this AspectF aspect, int milliseconds)
{
return aspect.Combine((work) =>
{
System.Threading.Thread.Sleep(milliseconds);
work();
});
}
[DebuggerStepThrough]
public static AspectF MustBeNonNull(this AspectF aspect,params object[] args)
{
return aspect.Combine((work) =>
{
for (int i = 0; i < args.Length; i++)
{
object arg = args[i];
if (arg==null)
{
throw new ArgumentException(string.Format("Parameter at index {0} is null", i));
}
}
work();
});
}
public static AspectF MustBeNonDefault<T>(this AspectF aspect, params T[] args) where T : IComparable
{
return aspect.Combine((work) =>
{
T defaultvalue = default(T);
for (int i = 0; i < args.Length; i++)
{
T arg = args[i];
if (arg==null||arg.Equals(defaultvalue))
{
throw new ArgumentException(string.Format("Parameter at index {0} is null", i));
}
}
work();
});
}
public static AspectF WhenTrue(this AspectF aspect, params Func<bool>[] conditions)
{
return aspect.Combine((work) =>
{
foreach (Func<bool> condition in conditions)
{
if (!condition())
{
return;
}
}
work();
});
}
[DebuggerStepThrough]
public static AspectF RunAsync(this AspectF aspect, Action completeCallback)
{
return aspect.Combine((work) => work.BeginInvoke(asyncresult =>
{
work.EndInvoke(asyncresult); completeCallback();
}, null));
}
[DebuggerStepThrough]
public static AspectF RunAsync(this AspectF aspect)
{
return aspect.Combine((work) => work.BeginInvoke(asyncresult =>
{
work.EndInvoke(asyncresult);
}, null));
}
}
缺點(diǎn):性能問題,首先是對象序列和反序列化的開銷,其次是對象強(qiáng)制轉(zhuǎn)換的開銷,最后是傳統(tǒng)aop反射的開銷。不過對于一般項(xiàng)目利遠(yuǎn)遠(yuǎn)大于弊。
再說DTO,這個概念和我們上述的場景完全是相反的論調(diào)。了解j2ee的同學(xué)對這些術(shù)語都很熟悉,Data Transfer Object數(shù)據(jù)傳輸對象,主要用于遠(yuǎn)程調(diào)用等需要大量傳輸對象的地方。比如我們一張表有20個字段,那么對應(yīng)的持久對象(persistant object,簡稱po)就有20個屬性。但是我們界面上只要顯示10個字段,應(yīng)用端用webservice來獲取數(shù)據(jù),沒有必要把整個PO對象傳遞到應(yīng)用端,這時我們就可以用只有這10個屬性的DTO來傳遞結(jié)果到客戶端。
前文我們介紹AOPResult的時候,不考慮帶寬也不考慮數(shù)據(jù)的安全策略。實(shí)際場景下,DTO的設(shè)計也需要考慮很多方面。我自己也不敢說自己設(shè)計的就是最合理的,我只分享下我的設(shè)計經(jīng)驗(yàn),在數(shù)據(jù)庫通用(各個系統(tǒng)都能訪問)的前提下,DTO只提供ID等表示字段,或者根據(jù)業(yè)務(wù)場景,如果應(yīng)用端需要的內(nèi)容不多可以設(shè)計成冗余類型甚至設(shè)計成PO。在帶寬充足的場景下例如內(nèi)網(wǎng)項(xiàng)目,放心的填充吧,沒那么多性能元素需要考慮。一般場景下,則需要考慮的多些,看方法的使用頻度,公網(wǎng)項(xiàng)目中dto中少存敏感字段等等。
題外
本篇先到此,有疑問或者有其他想法的同學(xué)歡迎提出來一起討論,下個章節(jié)一起再研究下如何利用泛型最簡單的操作多種類型數(shù)據(jù)庫。
![]() |
原創(chuàng)作品允許轉(zhuǎn)載,轉(zhuǎn)載時請務(wù)必以超鏈接形式標(biāo)明文章原始出處以及作者信息。 作者:熬夜的蟲子 點(diǎn)擊查看:博文索引 |

浙公網(wǎng)安備 33010602011771號