讓Dapper+SqlCE支持ntext數據類型和超過4000字符的存儲

使用Dapper和SqlCE進行開發的時候,若是數據庫的某字段是採用的ntext數據類型,而且在這個字段存儲的數據超過了4000個字符,會報以下的錯誤:html

Invalid parameter Size value '-1'. The value must be greater than or equal to 0.sql

在Google上以「dapper sqlce ntext」做爲關鍵詞搜索,能夠找到以下兩個解決辦法:數據庫

1. Inserting a string larger then 4000 characters using Sql CE 4.0app

2. Attempting to Modify Dapper to Support SQL Server CE's ntext typethis

這兩個解決辦法,要麼通用性較差,要麼使用比較麻煩。google

 

經過查看Dapper的源碼,發現Dapper在構造參數的動態方法中針對實體類屬性爲DbString的類型和數據類型爲DbType.Xml進行了特別處理,咱們也能夠在這裏入手,針對長字符串進行特別處理。spa

 

解決辦法:code

1、新建一個Attribute,用來標記須要特別處理的實體類屬性;server

    [AttributeUsage(AttributeTargets.Property)]
    public class LongStringAttribute : Attribute
    {
    }

2、新建一個類,用來處理添加NText類型參數;htm

    public class LongString
    {

        public static void AddParameter(IDbCommand command, string name, string value)
        {
            var param = command.CreateParameter();
            param.ParameterName = name;
            param.Value = (object)value ?? DBNull.Value;
            param.DbType = DbType.String;

            int length = -1;
            if (!string.IsNullOrEmpty(value))
                length = value.Length;
            if (length == -1 && value != null && value.Length <= 4000)
            {
                param.Size = 4000;
            }
            else
            {
                param.Size = length;
            }
            
            if (value != null)
            {
                if (length > 4000 && param.GetType().Name == "SqlCeParameter")
                {
                    param.GetType().GetProperty("SqlDbType").SetValue(param, SqlDbType.NText, null);
                    param.Size = length;
                }
            }

            command.Parameters.Add(param);
        }

    }

3、修改Dapper的源碼,在SqlMapper.CreateParamInfoGenerator方法中,找到如下代碼

          if (prop.PropertyType == typeof(DbString))
                {
                    il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param]
                    il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [dbstring]
                    il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [dbstring] [command]
                    il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [dbstring] [command] [name]
                    il.EmitCall(OpCodes.Callvirt, typeof(DbString).GetMethod("AddParameter"), null); // stack is now [parameters]
                    continue;
                }


                //咱們的代碼插入到這裏


                DbType dbType = LookupDbType(prop.PropertyType, prop.Name);
                if (dbType == DbType.Xml)
                {
                    // this actually represents special handling for list types;
                    il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [command]
                    il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [command] [name]
                    il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [command] [name] [typed-param]
                    il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [command] [name] [typed-value]
                    if (prop.PropertyType.IsValueType)
                    {
                        il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [command] [name] [boxed-value]
                    }
                    il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("PackListParameters"), null); // stack is [parameters]
                    continue;
                }

修改以後的代碼以下:

if (prop.PropertyType == typeof(DbString))
                {
                    il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param]
                    il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [dbstring]
                    il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [dbstring] [command]
                    il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [dbstring] [command] [name]
                    il.EmitCall(OpCodes.Callvirt, typeof(DbString).GetMethod("AddParameter"), null); // stack is now [parameters]
                    continue;
                }

                //這裏插入修改的代碼
                Attribute lStrAttr = Attribute.GetCustomAttribute(prop, typeof(LongStringAttribute));
                if (lStrAttr != null)
                {
                    //special handling for long string
                    il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [command]
                    il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [command] [name]
                    il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [command] [name] [typed-param]
                    il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [command] [name] [string]

                    il.EmitCall(OpCodes.Call, typeof(LongString).GetMethod("AddParameter"), null);
                    continue;
                }

                DbType dbType = LookupDbType(prop.PropertyType, prop.Name);
                if (dbType == DbType.Xml)
                {
                    // this actually represents special handling for list types;
                    il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [command]
                    il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [command] [name]
                    il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [command] [name] [typed-param]
                    il.Emit(OpCodes.Callvirt, prop.GetGetMethod()); // stack is [parameters] [command] [name] [typed-value]
                    if (prop.PropertyType.IsValueType)
                    {
                        il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [command] [name] [boxed-value]
                    }
                    il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("PackListParameters"), null); // stack is [parameters]
                    continue;
                }

 

這樣就能夠了,使用的時候,若是咱們數據表的某字段是ntext類型,那麼咱們只須要在定義相應實體類的時候,給相應屬性加上LongStringAttribute,Dapper就能夠自動識別這個字段,插入正確的數據了。

使用代碼以下:

//實體類定義
using System;
using Dapper;

namespace Entity
{
    public class product
    {
        public int Id { get; set; }
        public int shopid { get; set; }
        public string type { get; set; }
        public string outid { get; set; }
        public string link { get; set; }
        public string title { get; set; }
        [LongString]
        public string content { get; set; }
        public decimal price { get; set; }
        public int amount { get; set; }
    }
}


//調用

            product p = new product();
            p.shopid = 1;
            p.title = "梁振英:\"佔中\"者不要試探北京忍耐底線";
            p.link = "http://news.163.com/14/1021/13/A936JGST0001124J.html";
            string str = FileHelper.ReadTextFile("content.txt");
            p.content = str;
            p.type = "netease";
            p.outid = "A936JGST0001124J";
            p.price = 123.45M;
            p.amount = 999;

            SqlCeConnection conn = new SqlCeConnection("Data Source=test.sdf");
            conn.Open();

            string sql = "insert into products(shopid,type,outid,link,title,content,price,amount) values(@shopid,@type,@outid,@link,@title,@content,@price,@amount)";
            SqlMapper.Execute(conn, sql, p);

            conn.Close();

 

我使用的Dapper版本是博客園裏@wushilonng改寫的針對.NET 2.0的版本,未知最新版Dapper是否針對這方面作了改進。

 

------全文完-----

相關文章
相關標籤/搜索