且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

用C#打造自己的实体转换器

更新时间:2022-09-13 19:44:56

说明
尽管随着NoSQL的普及,数据库访问的性能已经非常关注的重点了(可以通过架构来解决这个瓶颈),所以有越来越多的项目使用了ORM来访问和操作数据库,在周公的博客上有一个系列的文章来比较ADO.NET和一些常见的ORM,实际上现在周公业余研究的一个项目中,周公也使用了MyBatisNet(由iBatisNet升级而来)。不过仍然有使用ADO.NET的场合,如果使用ADO.NET则免不了要写大量的将DataTable或者DataReader转换成对应的实体类的代码,经过了大约24小时的编码和测试(非连续的,累计的),周公尝试写了一个辅助工具,它可以将DataTable或者DataReader中的数据自动转换成实体类,这个辅助工具只有两个类,一个是负责转换的类,另一个是Attribute类,用以标识实体类的非静态属性与数据集中的数据列的对应关系。
为了便于使用,将所有代码写在了一个文件里,代码中有详尽的注释,所以在这里就不再介绍其原理和如何实现的了。完整的代码如下:
 


  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Data;  
  4. using System.Data.Common;  
  5. using System.Reflection;  
  6.  
  7. /// <summary>  
  8. /// 实体阅读器类,可以从DataTable中或者DbDataReader的实例中将数据转换成对应的示例  
  9. /// 作者:周公  
  10. /// 日期:2011-07-17  
  11. /// 修改日期:2011-07-21  
  12. /// 博客地址:http://blog.csdn.net/zhoufoxcn 或http://zhoufoxcn.blog.51cto.com  
  13. /// 说明:(1)任何人都可以免费使用,请尽量保持此段说明。  
  14. ///      (2)这个版本还不是最终版本,有任何意见或建议请到http://weibo.com/zhoufoxcn处留言。  
  15. /// </summary>  
  16. public sealed class EntityReader  
  17. {  
  18.     private const BindingFlags BindingFlag = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;  
  19.     //将类型与该类型所有的可写且未被忽略属性之间建立映射  
  20.     private static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyMappings = new Dictionary<Type, Dictionary<string, PropertyInfo>>();  
  21.     //存储Nullable<T>与T的对应关系  
  22.     private static Dictionary<Type, Type> genericTypeMappings = new Dictionary<Type, Type>();  
  23.  
  24.     static EntityReader()  
  25.     {  
  26.         genericTypeMappings.Add(typeof(Byte?), typeof(Byte));  
  27.         genericTypeMappings.Add(typeof(SByte?), typeof(SByte));  
  28.         genericTypeMappings.Add(typeof(Char?), typeof(Char));  
  29.         genericTypeMappings.Add(typeof(Boolean?), typeof(Boolean));  
  30.         genericTypeMappings.Add(typeof(Guid?), typeof(Guid));  
  31.         genericTypeMappings.Add(typeof(Int16), typeof(Int16));  
  32.         genericTypeMappings.Add(typeof(UInt16), typeof(UInt16));  
  33.         genericTypeMappings.Add(typeof(Int32), typeof(Int32));  
  34.         genericTypeMappings.Add(typeof(UInt32), typeof(UInt32));  
  35.         genericTypeMappings.Add(typeof(Int64), typeof(Int64));  
  36.         genericTypeMappings.Add(typeof(UInt64), typeof(UInt64));  
  37.         genericTypeMappings.Add(typeof(Single), typeof(Single));  
  38.         genericTypeMappings.Add(typeof(Double), typeof(Double));  
  39.         genericTypeMappings.Add(typeof(Decimal), typeof(Decimal));  
  40.         genericTypeMappings.Add(typeof(DateTime), typeof(DateTime));  
  41.         genericTypeMappings.Add(typeof(TimeSpan), typeof(TimeSpan));  
  42.         genericTypeMappings.Add(typeof(Enum), typeof(Enum));  
  43.  
  44.     }  
  45.     /// <summary>  
  46.     /// 将DataTable中的所有数据转换成List&gt;T&lt;集合  
  47.     /// </summary>  
  48.     /// <typeparam name="T">DataTable中每条数据可以转换的数据类型</typeparam>  
  49.     /// <param name="dataTable">包含有可以转换成数据类型T的数据集合</param>  
  50.     /// <returns></returns>  
  51.     public static List<T> GetEntities<T>(DataTable dataTable) where T : new()  
  52.     {  
  53.         if (dataTable == null)  
  54.         {  
  55.             throw new ArgumentNullException("dataTable");  
  56.         }  
  57.         //如果T的类型满足以下条件:字符串、ValueType或者是Nullable<ValueType>  
  58.         if(typeof(T)==typeof(string)||typeof(T).IsValueType)  
  59.         {  
  60.             return GetSimpleEntities<T>(dataTable);  
  61.         }  
  62.         else 
  63.         {  
  64.             return GetComplexEntities<T>(dataTable);  
  65.         }  
  66.     }  
  67.     /// <summary>  
  68.     /// 将DbDataReader中的所有数据转换成List&gt;T&lt;集合  
  69.     /// </summary>  
  70.     /// <typeparam name="T">DbDataReader中每条数据可以转换的数据类型</typeparam>  
  71.     /// <param name="dataTable">包含有可以转换成数据类型T的DbDataReader实例</param>  
  72.     /// <returns></returns>  
  73.     public static List<T> GetEntities<T>(DbDataReader reader) where T : new()  
  74.     {  
  75.         List<T> list = new List<T>();  
  76.         if (reader == null)  
  77.         {  
  78.             throw new ArgumentNullException("reader");  
  79.         }  
  80.         //如果T的类型满足以下条件:字符串、ValueType或者是Nullable<ValueType>  
  81.         if (typeof(T) == typeof(string) || typeof(T).IsValueType)  
  82.         {  
  83.             return GetSimpleEntities<T>(reader);  
  84.         }  
  85.         else 
  86.         {  
  87.             return GetComplexEntities<T>(reader);  
  88.         }  
  89.  
  90.     }  
  91.     /// <summary>  
  92.     /// 从DataTable中将每一行的第一列转换成T类型的数据  
  93.     /// </summary>  
  94.     /// <typeparam name="T">要转换的目标数据类型</typeparam>  
  95.     /// <param name="dataTable">包含有可以转换成数据类型T的数据集合</param>  
  96.     /// <returns></returns>  
  97.     private static List<T> GetSimpleEntities<T>(DataTable dataTable) where T : new()  
  98.     {  
  99.         List<T> list = new List<T>();  
  100.         foreach (DataRow row in dataTable.Rows)  
  101.         {  
  102.             list.Add((T)GetValueFromObject(row[0], typeof(T)));  
  103.         }  
  104.         return list;  
  105.     }  
  106.     /// <summary>  
  107.     /// 将指定的 Object 的值转换为指定类型的值。  
  108.     /// </summary>  
  109.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  110.     /// <param name="targetType">要转换的目标数据类型</param>  
  111.     /// <returns></returns>  
  112.     private static object GetValueFromObject(object value, Type targetType)  
  113.     {  
  114.         if (targetType == typeof(string))//如果要将value转换成string类型  
  115.         {  
  116.             return GetString(value);  
  117.         }  
  118.         else if (targetType.IsGenericType)//如果目标类型是泛型  
  119.         {  
  120.             return GetGenericValueFromObject(value, targetType);  
  121.         }  
  122.         else//如果是基本数据类型(包括数值类型、枚举和Guid)  
  123.         {  
  124.             return GetNonGenericValueFromObject(value, targetType);  
  125.         }  
  126.     }  
  127.  
  128.     /// <summary>  
  129.     /// 从DataTable中读取复杂数据类型集合  
  130.     /// </summary>  
  131.     /// <typeparam name="T">要转换的目标数据类型</typeparam>  
  132.     /// <param name="dataTable">包含有可以转换成数据类型T的数据集合</param>  
  133.     /// <returns></returns>  
  134.     private static List<T> GetComplexEntities<T>(DataTable dataTable) where T : new()  
  135.     {  
  136.         if (!propertyMappings.ContainsKey(typeof(T)))  
  137.         {  
  138.             GenerateTypePropertyMapping(typeof(T));  
  139.         }  
  140.         List<T> list = new List<T>();  
  141.         Dictionary<string, PropertyInfo> properties = propertyMappings[typeof(T)];  
  142.         //Dictionary<string, int> propertyColumnOrdinalMapping = GetPropertyColumnIndexMapping(dataTable.Columns, properties);  
  143.         T t;  
  144.         foreach (DataRow row in dataTable.Rows)  
  145.         {  
  146.             t = new T();  
  147.             foreach (KeyValuePair<string, PropertyInfo> item in properties)  
  148.             {  
  149.                 //int ordinal = -1;  
  150.                 //if (propertyColumnOrdinalMapping.TryGetValue(item.Key, out ordinal))  
  151.                 //{  
  152.                 //    item.Value.SetValue(t, GetValueFromObject(row[ordinal], item.Value.PropertyType), null);  
  153.                 //}  
  154.                 item.Value.SetValue(t, GetValueFromObject(row[item.Key], item.Value.PropertyType), null);  
  155.             }  
  156.             list.Add(t);  
  157.         }  
  158.         return list;  
  159.     }  
  160.  
  161.     /// <summary>  
  162.     /// 从DbDataReader的实例中读取复杂的数据类型  
  163.     /// </summary>  
  164.     /// <typeparam name="T">要转换的目标类</typeparam>  
  165.     /// <param name="reader">DbDataReader的实例</param>  
  166.     /// <returns></returns>  
  167.     private static List<T> GetComplexEntities<T>(DbDataReader reader) where T : new()  
  168.     {  
  169.         if (!propertyMappings.ContainsKey(typeof(T)))//检查当前是否已经有该类与类的可写属性之间的映射  
  170.         {  
  171.             GenerateTypePropertyMapping(typeof(T));  
  172.         }  
  173.         List<T> list = new List<T>();  
  174.         Dictionary<string, PropertyInfo> properties = propertyMappings[typeof(T)];  
  175.         //Dictionary<string, int> propertyColumnOrdinalMapping = GetPropertyColumnIndexMapping(reader, properties);  
  176.         T t;  
  177.         while (reader.Read())  
  178.         {  
  179.             t = new T();  
  180.             foreach (KeyValuePair<string, PropertyInfo> item in properties)  
  181.             {  
  182.                 //int ordinal = -1;  
  183.                 //if (propertyColumnOrdinalMapping.TryGetValue(item.Key, out ordinal))  
  184.                 //{  
  185.                 //    item.Value.SetValue(t, GetValueFromObject(reader[ordinal], item.Value.PropertyType), null);  
  186.                 //}  
  187.                 item.Value.SetValue(t, GetValueFromObject(reader[item.Key], item.Value.PropertyType), null);  
  188.             }  
  189.             list.Add(t);  
  190.         }  
  191.         return list;  
  192.     }  
  193.     /// <summary>  
  194.     /// 从DbDataReader的实例中读取简单数据类型(String,ValueType)  
  195.     /// </summary>  
  196.     /// <typeparam name="T">目标数据类型</typeparam>  
  197.     /// <param name="reader">DbDataReader的实例</param>  
  198.     /// <returns></returns>  
  199.     private static List<T> GetSimpleEntities<T>(DbDataReader reader)  
  200.     {  
  201.         List<T> list = new List<T>();  
  202.         while (reader.Read())  
  203.         {  
  204.             list.Add((T)GetValueFromObject(reader[0], typeof(T)));  
  205.         }  
  206.         return list;  
  207.     }  
  208.     /// <summary>  
  209.     /// 将Object转换成字符串类型  
  210.     /// </summary>  
  211.     /// <param name="value">object类型的实例</param>  
  212.     /// <returns></returns>  
  213.     private static object GetString(object value)  
  214.     {  
  215.         return Convert.ToString(value);  
  216.     }  
  217.  
  218.     /// <summary>  
  219.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  220.     /// </summary>  
  221.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  222.     /// <param name="targetType"></param>  
  223.     /// <returns></returns>  
  224.     private static object GetEnum(object value, Type targetType)  
  225.     {  
  226.         return Enum.Parse(targetType, value.ToString());  
  227.     }  
  228.  
  229.     /// <summary>  
  230.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  231.     /// </summary>  
  232.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  233.     /// <returns></returns>  
  234.     private static object GetBoolean(object value)  
  235.     {  
  236.         if (value is Boolean)  
  237.         {  
  238.             return value;  
  239.         }  
  240.         else 
  241.         {  
  242.             byte byteValue = (byte)GetByte(value);  
  243.             if (byteValue == 0)  
  244.             {  
  245.                 return false;  
  246.             }  
  247.             else 
  248.             {  
  249.                 return true;  
  250.             }  
  251.         }  
  252.     }  
  253.  
  254.     /// <summary>  
  255.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  256.     /// </summary>  
  257.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  258.     /// <returns></returns>  
  259.     private static object GetByte(object value)  
  260.     {  
  261.         if (value is Byte)  
  262.         {  
  263.             return value;  
  264.         }  
  265.         else 
  266.         {  
  267.             return byte.Parse(value.ToString());  
  268.         }  
  269.     }  
  270.  
  271.     /// <summary>  
  272.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  273.     /// </summary>  
  274.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  275.     /// <returns></returns>  
  276.     private static object GetSByte(object value)  
  277.     {  
  278.         if (value is SByte)  
  279.         {  
  280.             return value;  
  281.         }  
  282.         else 
  283.         {  
  284.             return SByte.Parse(value.ToString());  
  285.         }  
  286.     }  
  287.  
  288.     /// <summary>  
  289.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  290.     /// </summary>  
  291.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  292.     /// <returns></returns>  
  293.     private static object GetChar(object value)  
  294.     {  
  295.         if (value is Char)  
  296.         {  
  297.             return value;  
  298.         }  
  299.         else 
  300.         {  
  301.             return Char.Parse(value.ToString());  
  302.         }  
  303.     }  
  304.  
  305.     /// <summary>  
  306.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  307.     /// </summary>  
  308.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  309.     /// <returns></returns>  
  310.     private static object GetGuid(object value)  
  311.     {  
  312.         if (value is Guid)  
  313.         {  
  314.             return value;  
  315.         }  
  316.         else 
  317.         {  
  318.             return new Guid(value.ToString());  
  319.         }  
  320.     }  
  321.  
  322.     /// <summary>  
  323.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  324.     /// </summary>  
  325.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  326.     /// <returns></returns>  
  327.     private static object GetInt16(object value)  
  328.     {  
  329.         if (value is Int16)  
  330.         {  
  331.             return value;  
  332.         }  
  333.         else 
  334.         {  
  335.             return Int16.Parse(value.ToString());  
  336.         }  
  337.     }  
  338.  
  339.     /// <summary>  
  340.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  341.     /// </summary>  
  342.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  343.     /// <returns></returns>  
  344.     private static object GetUInt16(object value)  
  345.     {  
  346.         if (value is UInt16)  
  347.         {  
  348.             return value;  
  349.         }  
  350.         else 
  351.         {  
  352.             return UInt16.Parse(value.ToString());  
  353.         }  
  354.     }  
  355.  
  356.     /// <summary>  
  357.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  358.     /// </summary>  
  359.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  360.     /// <returns></returns>  
  361.     private static object GetInt32(object value)  
  362.     {  
  363.         if (value is Int32)  
  364.         {  
  365.             return value;  
  366.         }  
  367.         else 
  368.         {  
  369.             return Int32.Parse(value.ToString());  
  370.         }  
  371.     }  
  372.  
  373.     /// <summary>  
  374.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  375.     /// </summary>  
  376.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  377.     /// <returns></returns>  
  378.     private static object GetUInt32(object value)  
  379.     {  
  380.         if (value is UInt32)  
  381.         {  
  382.             return value;  
  383.         }  
  384.         else 
  385.         {  
  386.             return UInt32.Parse(value.ToString());  
  387.         }  
  388.     }  
  389.  
  390.     /// <summary>  
  391.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  392.     /// </summary>  
  393.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  394.     /// <returns></returns>  
  395.     private static object GetInt64(object value)  
  396.     {  
  397.         if (value is Int64)  
  398.         {  
  399.             return value;  
  400.         }  
  401.         else 
  402.         {  
  403.             return Int64.Parse(value.ToString());  
  404.         }  
  405.     }  
  406.  
  407.     /// <summary>  
  408.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  409.     /// </summary>  
  410.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  411.     /// <returns></returns>  
  412.     private static object GetUInt64(object value)  
  413.     {  
  414.         if (value is UInt64)  
  415.         {  
  416.             return value;  
  417.         }  
  418.         else 
  419.         {  
  420.             return UInt64.Parse(value.ToString());  
  421.         }  
  422.     }  
  423.  
  424.     /// <summary>  
  425.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  426.     /// </summary>  
  427.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  428.     /// <returns></returns>  
  429.     private static object GetSingle(object value)  
  430.     {  
  431.         if (value is Single)  
  432.         {  
  433.             return value;  
  434.         }  
  435.         else 
  436.         {  
  437.             return Single.Parse(value.ToString());  
  438.         }  
  439.     }  
  440.  
  441.     /// <summary>  
  442.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  443.     /// </summary>  
  444.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  445.     /// <returns></returns>  
  446.     private static object GetDouble(object value)  
  447.     {  
  448.         if (value is Double)  
  449.         {  
  450.             return value;  
  451.         }  
  452.         else 
  453.         {  
  454.             return Double.Parse(value.ToString());  
  455.         }  
  456.     }  
  457.  
  458.     /// <summary>  
  459.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  460.     /// </summary>  
  461.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  462.     /// <returns></returns>  
  463.     private static object GetDecimal(object value)  
  464.     {  
  465.         if (value is Decimal)  
  466.         {  
  467.             return value;  
  468.         }  
  469.         else 
  470.         {  
  471.             return Decimal.Parse(value.ToString());  
  472.         }  
  473.     }  
  474.  
  475.     /// <summary>  
  476.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  477.     /// </summary>  
  478.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  479.     /// <returns></returns>  
  480.     private static object GetDateTime(object value)  
  481.     {  
  482.         if (value is DateTime)  
  483.         {  
  484.             return value;  
  485.         }  
  486.         else 
  487.         {  
  488.             return DateTime.Parse(value.ToString());  
  489.         }  
  490.     }  
  491.  
  492.     /// <summary>  
  493.     /// 将指定的 Object 的值转换为指定枚举类型的值。  
  494.     /// </summary>  
  495.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  496.     /// <returns></returns>  
  497.     private static object GetTimeSpan(object value)  
  498.     {  
  499.         if (value is TimeSpan)  
  500.         {  
  501.             return value;  
  502.         }  
  503.         else 
  504.         {  
  505.             return TimeSpan.Parse(value.ToString());  
  506.         }  
  507.     }  
  508.  
  509.     /// <summary>  
  510.     /// 将Object类型数据转换成对应的可空数值类型表示  
  511.     /// </summary>  
  512.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  513.     /// <param name="targetType">可空数值类型</param>  
  514.     /// <returns></returns>  
  515.     private static object GetGenericValueFromObject(object value,Type targetType)  
  516.     {  
  517.         if (value == DBNull.Value)  
  518.         {  
  519.             return null;  
  520.         }  
  521.         else 
  522.         {  
  523.             //获取可空数值类型对应的基本数值类型,如int?->int,long?->long  
  524.             Type nonGenericType= genericTypeMappings[targetType];  
  525.             return GetNonGenericValueFromObject(value, nonGenericType);  
  526.         }  
  527.     }  
  528.  
  529.     /// <summary>  
  530.     /// 将指定的 Object 的值转换为指定类型的值。  
  531.     /// </summary>  
  532.     /// <param name="value">实现 IConvertible 接口的 Object,或者为 null</param>  
  533.     /// <param name="targetType">目标对象的类型</param>  
  534.     /// <returns></returns>  
  535.     private static object GetNonGenericValueFromObject(object value, Type targetType)  
  536.     {  
  537.         if (targetType.IsEnum)//因为  
  538.         {  
  539.             return GetEnum(value, targetType);  
  540.         }  
  541.         else 
  542.         {  
  543.             switch (targetType.Name)  
  544.             {  
  545.                 case "Byte"return GetByte(value);  
  546.                 case "SByte"return GetSByte(value);  
  547.                 case "Char"return GetChar(value);  
  548.                 case "Boolean"return GetBoolean(value);  
  549.                 case "Guid"return GetGuid(value);  
  550.                 case "Int16"return GetInt16(value) ;  
  551.                 case "UInt16"return GetUInt16(value);  
  552.                 case "Int32"return GetInt32(value);  
  553.                 case "UInt32"return GetUInt32(value);  
  554.                 case "Int64"return GetInt64(value);  
  555.                 case "UInt64"return GetUInt64(value);  
  556.                 case "Single"return GetSingle(value);  
  557.                 case "Double"return GetDouble(value);  
  558.                 case "Decimal"return GetDecimal(value);  
  559.                 case "DateTime"return GetDateTime(value);  
  560.                 case "TimeSpan"return GetTimeSpan(value);  
  561.                 defaultreturn null;  
  562.             }  
  563.         }  
  564.     }  
  565.  
  566.     /// <summary>  
  567.     /// 获取该类型中属性与数据库字段的对应关系映射  
  568.     /// </summary>  
  569.     /// <param name="type"></param>  
  570.     private static void GenerateTypePropertyMapping(Type type)  
  571.     {  
  572.         if (type != null)  
  573.         {  
  574.             PropertyInfo[] properties = type.GetProperties(BindingFlag);  
  575.             Dictionary<string, PropertyInfo> propertyColumnMapping = new Dictionary<string, PropertyInfo>(properties.Length);  
  576.             string description = string.Empty;  
  577.             Attribute[] attibutes = null;  
  578.             string columnName = string.Empty;  
  579.             bool ignorable = false;  
  580.             foreach (PropertyInfo p in properties)  
  581.             {  
  582.                 ignorable = false;  
  583.                 columnName = string.Empty;  
  584.                 attibutes = Attribute.GetCustomAttributes(p);  
  585.                 foreach (Attribute attribute in attibutes)  
  586.                 {  
  587.                     //检查是否设置了ColumnName属性  
  588.                     if (attribute.GetType() == typeof(ColumnNameAttribute))  
  589.                     {  
  590.                         columnName = ((ColumnNameAttribute)attribute).ColumnName;  
  591.                         ignorable = ((ColumnNameAttribute)attribute).Ignorable;  
  592.                         break;  
  593.                     }  
  594.                 }  
  595.                 //如果该属性是可读并且未被忽略的,则有可能在实例化该属性对应的类时用得上  
  596.                 if (p.CanWrite&&!ignorable)  
  597.                 {  
  598.                     //如果没有设置ColumnName属性,则直接将该属性名作为数据库字段的映射  
  599.                     if (string.IsNullOrEmpty(columnName))  
  600.                     {  
  601.                         columnName = p.Name;  
  602.                     }  
  603.                     propertyColumnMapping.Add(columnName, p);  
  604.                 }  
  605.             }  
  606.             propertyMappings.Add(type, propertyColumnMapping);  
  607.         }  
  608.     }  
  609.  
  610.     //private static Dictionary<string, int> GetPropertyColumnIndexMapping(DataColumnCollection dataSource, Dictionary<string, PropertyInfo> properties)  
  611.     //{  
  612.     //    Stopwatch watch = new Stopwatch();  
  613.     //    watch.Start();  
  614.     //    Dictionary<string,int> propertyColumnIndexMapping=new Dictionary<string,int>(dataSource.Count);  
  615.     //    foreach(KeyValuePair<string,PropertyInfo> item in properties)  
  616.     //    {  
  617.     //        for (int i = 0; i < dataSource.Count; i++)  
  618.     //        {  
  619.     //            if (item.Key.Equals(dataSource[i].ColumnName, StringComparison.InvariantCultureIgnoreCase))  
  620.     //            {  
  621.     //                propertyColumnIndexMapping.Add(item.Key, i);  
  622.     //                break;  
  623.     //            }  
  624.     //        }  
  625.     //    }  
  626.     //    watch.Stop();  
  627.     //    Debug.WriteLine("Elapsed:" + watch.ElapsedMilliseconds);  
  628.     //    return propertyColumnIndexMapping;  
  629.     //}  
  630.  
  631.     //private static Dictionary<string, int> GetPropertyColumnIndexMapping(DbDataReader dataSource, Dictionary<string, PropertyInfo> properties)  
  632.     //{  
  633.     //    Dictionary<string, int> propertyColumnIndexMapping = new Dictionary<string, int>(dataSource.FieldCount);  
  634.     //    foreach (KeyValuePair<string, PropertyInfo> item in properties)  
  635.     //    {  
  636.     //        for (int i = 0; i < dataSource.FieldCount; i++)  
  637.     //        {  
  638.     //            if (item.Key.Equals(dataSource.GetName(i), StringComparison.InvariantCultureIgnoreCase))  
  639.     //            {  
  640.     //                propertyColumnIndexMapping.Add(item.Key, i);  
  641.     //                continue;  
  642.     //            }  
  643.     //        }  
  644.     //    }  
  645.     //    return propertyColumnIndexMapping;  
  646.     //}  
  647. }  
  648. /// <summary>  
  649. /// 自定义属性,用于指示如何从DataTable或者DbDataReader中读取类的属性值  
  650. /// </summary>  
  651. public class ColumnNameAttribute : Attribute  
  652. {  
  653.     /// <summary>  
  654.     /// 类属性对应的列名  
  655.     /// </summary>  
  656.     public string ColumnName { getset; }  
  657.     /// <summary>  
  658.     /// 指示在从DataTable或者DbDataReader中读取类的属性时是否可以忽略这个属性  
  659.     /// </summary>  
  660.     public bool Ignorable { getset; }  
  661.     /// <summary>  
  662.     /// 构造函数  
  663.     /// </summary>  
  664.     /// <param name="columnName">类属性对应的列名</param>  
  665.     public ColumnNameAttribute(string columnName)  
  666.     {  
  667.         ColumnName = columnName;  
  668.         Ignorable = false;  
  669.     }  
  670.     /// <summary>  
  671.     /// 构造函数  
  672.     /// </summary>  
  673.     /// <param name="ignorable">指示在从DataTable或者DbDataReader中读取类的属性时是否可以忽略这个属性</param>  
  674.     public ColumnNameAttribute(bool ignorable)  
  675.     {  
  676.         Ignorable = ignorable;  
  677.     }  
  678.     /// <summary>  
  679.     /// 构造函数  
  680.     /// </summary>  
  681.     /// <param name="columnName">类属性对应的列名</param>  
  682.     /// <param name="ignorable">指示在从DataTable或者DbDataReader中读取类的属性时是否可以忽略这个属性</param>  
  683.     public ColumnNameAttribute(string columnName, bool ignorable)  
  684.     {  
  685.         ColumnName = columnName;  
  686.         Ignorable = ignorable;  
  687.     }  

用法举例
这里以MySQL为例(因为公司不允许安装破解和盗版软件,所以我的电脑上只有免费的SQLite和MySQL社区版了)。
 


  1. MySqlConnection connection = new MySqlConnection("Server=localhost;Database=crawldb;Uid=root;Pwd=root;Port=3306;"); 
  2. MySqlCommand command = new MySqlCommand("SELECT * FROM TY_Content order by Id desc limit 0,200000", connection); 
  3. MySqlDataAdapter adapter = new MySqlDataAdapter(command); 
  4. DataTable data6 = new DataTable(); 
  5. adapter.Fill(data6); 
  6. Console.WriteLine("MySQL"); 
  7. watch.Reset(); 
  8. watch.Start(); 
  9. List<TY_ContentInfo> list6 = EntityReader.GetEntities<TY_ContentInfo>(data6); 
  10. watch.Stop(); 
  11. Console.WriteLine("Parse data in DataTable lasts ms:{0}", watch.ElapsedMilliseconds); 
  12. Console.WriteLine("Data record Count:{0}", list6.Count); 
  13. data6.Clear(); 
  14. data6.Dispose(); 
  15. list6.Clear(); 
  16. list6 = null

性能测试数据
在SQLite上有表如下:


  1. CREATE TABLE TY_Content ( 
  2.    Id            integer NOT NULL PRIMARY KEY UNIQUE
  3.    ArticleId            int                  not null
  4.    Content              text                 not null
  5.    ContentHash          varchar(32)          not null
  6.    CreateAt             datetime             null default CURRENT_TIMESTAMP 
  7. ); 
  8.  
  9. CREATE UNIQUE INDEX IDX_ContentHash on TY_Content ( 
  10. ContentHash ASC 
  11. ); 

里面共有数据128062条,采用DataTable和SQLiteDataReader的方式读取耗时分别为1582ms和11120ms。
在MySQL中也存在有上面结构的表,有数据175616条,采用DataTable和SQLiteDataReader的方式读取耗时分别为1958ms和9461ms。
在数据库记录条数百万级以下,使用它还可以可以的,实际上在真实的开发中不可能一次性读取数十万条数据的,因此还可以可用于一般的中小型网站。


未尽事宜
第一个未尽事宜就是我还在分析为什么从DataTable中读取比DataReader中读取要快,即使不同数据库都是如此,几乎相差一个数量级,这跟平时我们印象中“从DataReader中读数据要比DataTable中快”相冲突。
第二个未尽事宜是代码的优化问题,因为最近个人事情比较多,还有很多地方没有来得及优化,甚至我的另一种实现还没有比较,那就是采用分而治之的方式,针对每一种数据类型都写一种方法来处理,这样就可以去掉反射部分了,或许速度上还会有提高,但这还是我的预想,没有验证。
总结

上面是一个逻辑稍微有点复杂的例子,里面用到了反射、Attribute及ADO.NET的例子,可以作为想学习这方面知识的朋友一个范例。除此之外,还可以将这个类用于中小型系统开发中。
如果大家有什么好的意见或建议,或者发现了bug,请在博客下方留言或者到http://weibo.com/zhoufoxcn上留言。


















本文转自周金桥51CTO博客,原文链接:http://blog.51cto.com/zhoufoxcn/614794 ,如需转载请自行联系原作者