也來寫寫基於單表的Orm(使用Dapper)


前言

  這兩天看園子里有個朋友寫Dapper的拓展,想到自己之前也嘗試用過,但不順手,曾寫過幾個方法來完成自動的Insert操作。而對於Update、Delete、Select等,我一直對Dictionary<string,object>參數類型有很深的感情,旨在頁面傳遞的條件基本上都是Json,很容易用字典類型存儲,想過在遵循某種命名規則的前提下,自動完成查詢,簡單的業務,直接能得到相應條件的數據,為此研究過一段時間,如果需要詳細的了解,請看這里:

  理想中的SQL條件拼接方式

  理想中的SQL條件拼接方式(二)

  看了下文章的時間,嚇我一條,居然在2個月前,事實上,這個念頭在心里也一直有很長時間了,卻覺得自己很難去處理好它,封裝的沒問題,但用起來特別不順手。如今新年來臨,再來挑戰自己。

  之前遇到的最大的問題就是處理值與類型的映射。由於頁面傳遞的是Json數據源,后來序列化成Dictionary<string,object>后,很多類型就丟失了。如直接引起程序錯誤的DateTime類型了。原來的處理方式是各種手動的映射,更改數據類型,再提交ado.net操作。

  如今查看Poto這個Orm的源代碼,它在實體增刪改的時候將類型緩存起來,而且還有該類型的屬性,在使用的時候,做過幾次轉換,而我像發現新大陸似得,覺得這完全可以解決我的問題呀。於是照搬了它的緩存部分代碼,並結合Dapper實現了實體的增刪改查,當然查詢是通過Dictionary的傳參。

對Dapper和Poto的拓展與修改

  1.在Dapper的1219行代碼處有方法 GetCacheInfo,在其中增加IDictionary參數支持。修改后的方法為:

        private static CacheInfo GetCacheInfo(Identity identity)
        {
            CacheInfo info;
            if (!TryGetQueryCache(identity, out info))
            {
                info = new CacheInfo();
                if (identity.parametersType != null)
                {
                    if (typeof(IDynamicParameters).IsAssignableFrom(identity.parametersType))
                    {
                        info.ParamReader = (cmd, obj) => { (obj as IDynamicParameters).AddParameters(cmd, identity); };
                    }
#if !CSHARP30
                    else if ( //支持傳遞Dictionary<K,V>為參數
                        typeof(IDictionary).IsAssignableFrom(identity.parametersType) ||
                        typeof(IEnumerable<KeyValuePair<string, object>>).IsAssignableFrom(identity.parametersType)
                        && typeof(System.Dynamic.IDynamicMetaObjectProvider).IsAssignableFrom(identity.parametersType)
                        )
                    {
                        info.ParamReader = (cmd, obj) =>
                        {
                            IDynamicParameters mapped = new DynamicParameters(obj);
                            mapped.AddParameters(cmd, identity);
                        };
                    }
#endif
                    else
                    {
                        info.ParamReader = CreateParamInfoGenerator(identity, false);
                    }
                }
                SetQueryCache(identity, info);
            }
            return info;
        }
GetCacheInfo

  2.在Poto中,增加了OrderAttribute,定義在實體上,指定自動生成的Select排序規則。

再談Orm的具體實現

  基於實體的增刪改,參數是object,則可以根據對象獲取到它的類型、屬性,還有定義在類上的Attribute。很容易就可以構造出 insert、update、delete。另外由於Dapper支持一次SQL,在多個對象上執行,我對方法做了這樣的改進,以充分利用Dapper。解釋下,一次SQL,在多個對象上執行,如批量Insert,批量Update,批量Delete。SQL語句使用參數化,則是同一條,執行時參數來自於不同的對象而已。所以增刪改查,參數為object時,還可以傳遞List、數組等。

  在Update和Delete方法中,我還希望通過條件來操作,運用到上面說的基於命名規則拼接SQL的方式,當然Select操作也不例外。主要實現代碼:

        private readonly Regex reg = new Regex(@"^(?<tab>\w+\.)?(?<Pre>(PK|Begin|End|Like|UnLike|Null|In))?(?<Key>\w+)$");

        private string CreateWhere(PocoData t, IDictionary<string, object> condition)
        {
            var sql = new StringBuilder();

            //遍歷條件
            foreach (var key in condition.Keys.ToArray())
            {
                var match = reg.Match(key); //正則獲取到屬性對應的字段名
                var pre = match.Groups["Pre"].Value;//獲取命名規則中的比較謂詞
                var colName = match.Groups["Key"].Value;//獲取字段名稱
                //if (string.IsNullOrEmpty(tableName))
                //    tableName = match.Groups["tab"].Value;//獲取字段表名限定

                var paraName = string.Format("{0}{1}", pre, colName);//獲取該條件被分配的過程參數名

                var value = condition[key];

                //拼接Sql語句
                sql.AppendFormat(" {0} AND",
                    Builder(pre, colName, paraName, t.TableInfo.TableName, value));//拼接SQL條件

                if (pre == "In") //In拼接沒有使用過程參數,直接拼接了,將參數從字典中移除
                {
                    condition.Remove(key);
                    continue;
                }

                //若該字段值的類型為字符串,檢測實體中的類型
                if (value.GetType() == typeof(string))
                {
                    PocoColumn prop;
                    if (!t.Columns.TryGetValue(colName, out prop))
                        throw new Exception(string.Format("can't find any property in object.propertyName:{0},object:{1}", key, t.TableInfo.TableName));

                    if (prop.PropertyInfo.PropertyType != typeof(string))
                        condition[key] = prop.ChangeType(value);
                }
            }

            if (sql.Length > 3) sql.Length -= 3;
            return sql.ToString();
        }
正則拆分SQL

 說到這里,有個小插曲,我發現當我在SQL和Oracle下測試成功后,我傳遞的Dictionary,並沒有指定參數符號,如@或者: 。最后執行的DbCommand對象使用的參數名當然也沒有,可居然沒有出現任何問題。不知道該怎么解釋。這里Sql的庫使用的System.Data.SqlClient, net4.0 。而Oracle是使用的官方的,Oracle.ManagedDataAccess.dll ,net4.0。如果有朋友知道,還請告知一二。雖然我想偷懶,但這種突如其來的幸福,還是警惕下。

  最后在Update方法上增加參數,指定更新的列。

其他

  解釋下什么是基於單表的Orm。單表操作,增刪改查,所有的方法都是在同一個表或者實體上。由於目前我還沒有看Dapper或者Poco在多表下是怎么操作的,而暫時我又需要緊急的使用在項目中,因為我不想在寫增刪改查,簡單業務下linq式的查詢我也不想寫。好吧,我承認我有點魯莽,不穩定的東西就投入使用,可實踐就是檢驗真理的唯一標准啊。說到實踐,關於Poto這個Orm,我看其他人的介紹,經過各種測試,當我用了,我只想說,這測試不仔細,Oracle下各種錯誤,連自動分頁都是錯的。

  寫到這里,發現具體的實現方式,已然不值一提。在寫之前,各種困難,寫之后,想要介紹它的時候,發現太簡單,沒什么可說的。代碼中有一點注釋,想看的朋友如果看不明白可以咨詢。

  先貼下封裝后的調用方式。

            var db = new DbHelp(Program.facotry, Program.connStr);

            ///////////////////////////////////////////////////////////////////////
            var tabAdd = new tab1()
            {
                Name = "ggggg",
                Home = "jjjjjj"
            };
            var row = db.Add(tabAdd);
            //批量增
            var tabAddArrary = new tab1[] { 
            new tab1()
            {
                Name = "批量增1",
                Home = "批量增1"
            },
             new tab1()
            {
                Name = "批量增2",
                Home = "批量增2"
            }};
            row = db.Add(tabAddArrary);

            ////////////////////////////////////////////////////////////////////////////
            var tabUpdate = new tab1()
            {
                Name = "fffffff",
                Home = "fffffff",
                ID = 5
            };
            row = db.Update(tabUpdate);
            //批量改
            var tabUpdateArrary = new tab1[] { 
            new tab1()
            {
                Name = "批量改1",
                Home = "批量改1",
                ID=10
            },
             new tab1()
            {
                Name = "批量改2",
                Home = "批量改2",
                ID=11
            }};
            row = db.Update(tabUpdateArrary);
            //條件修改
            //1 定義要修改的內容
            var dic = new Dictionary<string, object>();
            dic["Home"] = DateTime.Now.ToString() + ":修改了";
            //定義條件 這里使用了命名規則
            var condtion = new Dictionary<string, object>();
            condtion["InID"] = "1,3,5";
            row = db.Update<tab1>(dic, condtion);

            //刪除///////////////////////////////////////////////////
            var tabDelete = new tab1() { ID = 10 };
            db.Remove(tabDelete);
            //條件刪除
            var dicDelete = new Dictionary<string, object>();
            dicDelete["BeginID"] = 50;
            row = db.Remove<tab1>(dicDelete);

            //查詢/////////////////////////////////////////////////////
            var dicQuery = new Dictionary<string, object>();
            dicQuery["EndID"] = 50;
            var qtab1 = db.Query<tab1>(dicQuery);

            //分頁///////////////////////////////////////////////////////////
            int count;
            var dicPage = new Dictionary<string, object>();
            dicPage["BeginID"] = 50;
            dicPage["EndID"] = 10;
            var qtabPage = db.Query<tab1>(dicQuery, 3, 5, out count);
封裝后的調用

  測試代碼下載

   最后祝大家馬上有錢。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM