傳說中的dynamic
dynamic是個不合群、不按規則辦事的傢伙,可以說是個異形,但更恐怖的是它又是無所不知的,任何事情都難不了它(咳咳,它似乎與Lambda運算式是死對頭)。這令人想起《死亡日記》的怪異偵探L,行為怪異而智力超人,以至於離奇的案例不得不交給了他。dynamic可以看成是一切類型的化身,但並不是僅限於此,它像《未來戰士》續集裡面的T-1000型液體金屬的終結者。噢~~~~似乎扯的有點遠了
飽經風雨而不倒的ADO.NET
ADO.NET 從來做事都有理有據,而且又異常專註於自身領域,是個professional的牛人,令人想起《美麗心靈》裡面的博弈論和微分幾何學領域潛心研究以致獲得諾貝爾經濟學獎的數學家—— 約翰·福布斯·納什 教授(咳咳,納什教授是個妄想型精神分裂的~~~嗯,這個以後再說)。
關於ADO.NET 的例子
1. 執行SQL語句
using (DbCommand command = connection.CreateCommand()){ command.CommandText = "select Top 10 * from Orders"; command.CommandType = CommandType.Text; using (IDataReader reader = command.ExecuteReader()) { while (reader.Read()) { Console.WriteLine("OrderID: {0}, OrderDate: {1}", reader.GetInt32(reader.GetOrdinal("OrderID")), reader.GetDateTime(reader.GetOrdinal("OrderDate"))); } }}
2. 調用預存程序
command.CommandText = "CustOrdersOrders"; command.CommandType = CommandType.StoredProcedure; command.Parameters.Add(new SqlParameter("CustomerID", "ALFKI")); //略去...
當 ADO.NET 遇上 dynamic
某年某月ADO.NET不幸遇到dynamic,從此循規蹈矩的生活不複存在。dynamic說它可以協助 ADO.NET 丟掉 DataSet 的包袱,而且在不用建立資料實體的情況下,實現查詢結果垮不同方法傳遞;更加強大的地方是可以與預存程序無縫串連,即像調用一般方法一樣調用預存程序而不用寫額外代碼。我的神哪~~~ ADO.NET 聽了dynamic一番遊說後,心底下不禁驚訝一下。dynamic又說,實現剛才所說的工程只要借你手下的兩大猛將 SqlConnection 和 SqlCommand 助我一臂之力即可。
dynamic真有如此奇技? ADO.NET 雖有懷疑,但它想到曾經看過一部叫《阿甘正傳》的電影,裡面的阿甘雖然是弱智人,但參軍時練就乒乓奇技,後來還和中國國手同台競技。想到這,ADO.NET 認為不能因為對方弱智就不相信對方的話,這是很不禮貌很不紳士的人才會做的事,所以它相信了dynamic。
dynamic 果真不負眾望,三兩腳貓功夫就交出成果了。
dynamic重構後的資料庫操作
using (dynamic command = connection.CreateDynamicCommand()){ //執行查詢SQL IEnumerable<dynamic> toptenOrders = command("select Top 10 * from Orders"); foreach (dynamic order in toptenOrders) { Console.WriteLine("OrderID: {0}, OrderDate: {1}", order.OrderID, order.OrderDate); } //執行帶參數的SQL IEnumerable<dynamic> customerOrders = command("select * from Orders where CustomerID = @CustomerID", CustomerID: "ALFKI"); foreach (dynamic order in customerOrders) { Console.WriteLine("OrderID: {0}, OrderDate: {1}", order.OrderID, order.OrderDate); } //調用預存程序 IEnumerable<dynamic> orders = command.CustOrdersOrders(CustomerID: "ALFKI"); foreach (dynamic order in orders) { Console.WriteLine("OrderID: {0}, OrderDate: {1}", order.OrderID, order.OrderDate); }}
要知道 ADO.NET 可不是.NET菜鳥,它看到 command("select Top 10 * from Orders"); 第一感覺認為吃了dynamic藥的command有可能是委託類型,而看到後面的 command.CustOrdersOrders(CustomerID: "ALFKI"); 不得不否決了前面的看法。dynamic到底是什麼東西?可以這樣認為,dynamic什麼東西都是;也可以認為,dynamic不是什麼東西!
ADO.NET 知道任何.NET寫的再高深的代碼在reflector下都會現出原形,通過對 command 解剖,立刻明白原來自己跟《美麗心靈》的納什教授一樣糾纏於一種不存在的幻想不能自拔,reflector告訴我們:dynamic實際上是不存在的!
還是魯迅叔叔說的好,世界上本沒有dynamic,只是微軟對委託封裝得太牛了,也便有了dynamic。
結語
聰明的你知道command是怎麼實現了嗎?不妨先想想,然後展開下面的代碼看看是否與你想的一致。
點此展開代碼
註:本文預存程序部分參考了微型ORM.
public static class Extensions{ public static DynamicCommand CreateDynamicCommand(this DbConnection connection) { return new DynamicCommand(connection); }}
/// <summary> /// 動態Command /// </summary> public class DynamicCommand : DynamicObject, IDisposable { public DbConnection Connection { get; set; } public DynamicCommand(DbConnection connection) { this.Connection = connection; } //實現SQL語句查詢 public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) { if (args.Length == 0) throw new ArgumentException("args must has value"); result = Execute(args[0].ToString(), CommandType.Text, binder.CallInfo.ArgumentNames, args.Skip(1).ToArray()); return true; } //實現預存程序 public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (binder.CallInfo.ArgumentNames.Count != binder.CallInfo.ArgumentCount) { throw new ArgumentException("All parameters must be named"); } result = Execute(binder.Name, CommandType.StoredProcedure, binder.CallInfo.ArgumentNames, args); return true; } /// <summary> /// 執行SQL查詢 /// </summary> /// <param name="commandText"></param> /// <param name="commandType"></param> /// <param name="names"></param> /// <param name="args"></param> /// <returns></returns> private object Execute(string commandText, CommandType commandType, IEnumerable<string> names, object[] args) { bool manageConnectionLifespan = (this.Connection.State == ConnectionState.Closed); if (manageConnectionLifespan) this.Connection.Open(); try { using (var cmd = this.Connection.CreateCommand()) { cmd.CommandType = commandType; cmd.CommandText = commandText; for (int i = 0; i < args.Length; i++) { DbParameter param = cmd.CreateParameter(); param.ParameterName = "@" + names.ElementAt(i); param.Value = args[i] == null ? DBNull.Value : args[i]; cmd.Parameters.Add(param); } return ExecuteList(cmd); } } finally { if (manageConnectionLifespan) { this.Connection.Close(); } } } /// <summary> /// 執行SQL命令,返回查詢結果清單 /// </summary> /// <param name="command"></param> /// <returns></returns> private static IEnumerable<dynamic> ExecuteList(DbCommand command) { List<DynamicEntity> resultList = new List<DynamicEntity>(); using (DbDataReader reader = command.ExecuteReader()) { while (reader.Read()) { DynamicEntity entity = new DynamicEntity(); for (int i = 0; i < reader.FieldCount; i++) { entity.SetMember(reader.GetName(i), reader.GetValue(i)); } resultList.Add(entity); } } return resultList; } }
/// <summary>/// 動態實體/// </summary>internal class DynamicEntity : DynamicObject{ /// <summary> /// 屬性和值的字典表 /// </summary> private Dictionary<string, object> values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); public override bool TryGetMember(GetMemberBinder binder, out object result) { if (values.ContainsKey(binder.Name)) { result = values[binder.Name]; } else { throw new System.MissingMemberException("The property " + binder.Name + " does not exist"); } return true; } public override bool TrySetMember(SetMemberBinder binder, object value) { SetMember(binder.Name, value); return true; } public override IEnumerable<string> GetDynamicMemberNames() { return values.Keys; } internal void SetMember(string propertyName, object value) { if (object.ReferenceEquals(value, DBNull.Value)) { values[propertyName] = null; } else { values[propertyName] = value; } }}