zoukankan      html  css  js  c++  java
  • 浅尝辄止:通过DynamicMethod和ILGenerator创建实体类

    周末尝试着学习emit进行高级编程。想起若干天前收藏的Herbrandson在codeproject上的神作(此文技术含量其实一般,实际上他讲的就是用DynamicMethodILGenerator实现快速实体创建的方法,但是文笔睿智,用词精致,非常诱人),重温了一下原文,又参考了msdn和园子里几位高手的博客,整理一下自己的学习笔记,希望对您有用。

    一、常见的将DataReader转换为List的方法
    1、类似codesmith的模板生成方式
    熟悉代码生成器的童鞋一定知道的。在实体类里生成了“一堆”字段,属性,数据类型等等相关的判断(不是switch...case就是if...else等等等等)。其实也就类似Herbranson原文里介绍的第一种实现方式。毫无疑问,这种方式简单直接明了,是个程序员都看得懂,但是多数人会嫌弃代码啰嗦,生成的类会显得非常臃肿庞大。
    代码
    public class ManualBuilder
    {
        
    public Person Build(SqlDataReader reader)
        {
            Person person 
    = new Person();

            
    if (!reader.IsDBNull(0))
            {
                person.ID 
    = (Guid)reader[0];
            }

            
    if (!reader.IsDBNull(1))
            {
                person.Name 
    = (string)reader[1];
            }

            
    if (!reader.IsDBNull(2))
            {
                person.Kids 
    = (int)reader[2];
            }

            
    if (!reader.IsDBNull(3))
            {
                person.Active 
    = (bool)reader[3];
            }

            
    if (!reader.IsDBNull(4))
            {
                person.DateOfBirth 
    = (DateTime)reader[4];
            }

            
    return person;
        }
    }

    2、反射,反射,兴高采烈地登场
    大家都觉得上面第一种的实现代码毫无美感,通过反射,世界就清静多了:

    代码
    public class ReflectionBuilder</>
    {
        
    private PropertyInfo[] properties;

        
    private ReflectionBuilder() { }

        
    public T Build(SqlDataReader reader)
        {
            T result 
    = (T)Activator.CreateInstance(typeof(T));

            
    for (int i = 0; i < reader.FieldCount; i++)
            {
                
    if (properties[i] != null && !reader.IsDBNull(i))
                {
                    properties[i].SetValue(result, reader[i], 
    null);
                }
            }

            
    return result;
        }

        
    public static ReflectionBuilder<t> CreateBuilder(SqlDataReader reader)
        {
            ReflectionBuilder
    <t> result = new ReflectionBuilder<t>();

            result.properties 
    = new PropertyInfo[reader.FieldCount];
            
    for (int i = 0; i < reader.FieldCount; i++)
            {
                result.properties[i] 
    = typeof(T).GetProperty(reader.GetName(i));
            }

            
    return result;
        }
    }

    观察上面的代码,大家会发现程序的通用性大大增强,减少了大量重复的工作,技术含量也是杠杠的。
    ps:前面两种方式的实现代码都是原文里拷贝来的。大家最常见的就是这两种方式,所以懒得写注释了,没什么难度的。
    ps1:通过反射转换实体,其实还有其他写法的,但是主要思想大同小异(厚颜推荐楼猪的关于ado.net的旧文)。

    二、来一点点emit,性能提高不是一点点
    这就是传说已久的通过emit创建动态代理,实现实体的创建:

    代码
    // ========================================================================================
    // Information: provide by Herbrandson
    // Source: http://www.codeproject.com/KB/database/DynamicMethod_ILGenerator.aspx
    // ========================================================================================
    using System;
    using System.Data;
    using System.Reflection;
    using System.Reflection.Emit;

    /// <summary>
    ///Use DynamicMethod and ILGenerator create entity
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DynamicBuilder<T>
    {
        
    private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item"new Type[] { typeof(int) });

        
    private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull"new Type[] { typeof(int) });

        
    private delegate T Load(IDataRecord dataRecord);

        
    private Load handler;//最终执行动态方法的一个委托 参数是IDataRecord接口

        
    private DynamicBuilder() { }//私有构造函数

        
    public T Build(IDataRecord dataRecord)
        {
            
    return handler(dataRecord);//执行CreateBuilder里创建的DynamicCreate动态方法的委托
        }

        
    public static DynamicBuilder<T> CreateBuilder(IDataRecord dataRecord)
        {
            DynamicBuilder
    <T> dynamicBuilder = new DynamicBuilder<T>();

            
    //定义一个名为DynamicCreate的动态方法,返回值typof(T),参数typeof(IDataRecord)
            DynamicMethod method = new DynamicMethod("DynamicCreate"typeof(T), new Type[] { typeof(IDataRecord) }, typeof(T), true);

            ILGenerator generator 
    = method.GetILGenerator();//创建一个MSIL生成器,为动态方法生成代码

            LocalBuilder result 
    = generator.DeclareLocal(typeof(T));//声明指定类型的局部变量 可以T t;这么理解

            
    //The next piece of code instantiates the requested type of object and stores it in the local variable. 可以t=new T();这么理解
            generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
            generator.Emit(OpCodes.Stloc, result);

            
    for (int i = 0; i < dataRecord.FieldCount; i++)//数据集合,熟悉的for循环 要干什么你懂的 
            {
                PropertyInfo propertyInfo 
    = typeof(T).GetProperty(dataRecord.GetName(i));//根据列名取属性  原则上属性和列是一一对应的关系
                Label endIfLabel = generator.DefineLabel();

                
    if (propertyInfo != null && propertyInfo.GetSetMethod() != null)//实体存在该属性 且该属性有SetMethod方法
                {
                    
    /*The code then loops through the fields in the data reader, finding matching properties on the type passed in. 
                     * When a match is found, the code checks to see if the value from the data reader is null.
                     
    */
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, isDBNullMethod);
    //就知道这里要调用IsDBNull方法 如果IsDBNull==true contine
                    generator.Emit(OpCodes.Brtrue, endIfLabel);

                    
    /*If the value in the data reader is not null, the code sets the value on the object.*/
                    generator.Emit(OpCodes.Ldloc, result);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, getValueMethod);
    //调用get_Item方法
                    generator.Emit(OpCodes.Unbox_Any, dataRecord.GetFieldType(i));
                    generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
    //给该属性设置对应值

                    generator.MarkLabel(endIfLabel);
                }
            }

            
    /*The last part of the code returns the value of the local variable*/
            generator.Emit(OpCodes.Ldloc, result);
            generator.Emit(OpCodes.Ret);
    //方法结束,返回

            
    //完成动态方法的创建,并且创建执行该动态方法的委托,赋值到全局变量handler,handler在Build方法里Invoke
            dynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load));
            
    return dynamicBuilder;
        }
    }

    (1)、因为老外的这个实现方式涉及到emit的知识,大部分开发者可能了解的并不是很深入,楼猪也不是很熟悉,而且楼猪极不习惯emit这种编程风格,感觉它非常破坏代码的美感,这种牺牲色相的高级写法如果在我们的项目中大面积出现,估计大部分童鞋都会抓狂的。好在现在终于有了工具可以“Emit with a human face”了。
    (2)、不管怎样,还是凭借自己学习到的一点知识储备,大胆按照自己的理解,写了点注释,对照原文作者的意思,不见得很烂,就是这么自信,呵呵。
    在外部调用的方式如下(测试代码,无比丑陋,请留意):

    代码
      static void Main(string[] args)
            {
                
    string strConn = @"Data Source=.\sqlexpress;Initial Catalog=TestDb;Persist Security Info=True;User ID=sa;Password=123456";
                
    using (SqlConnection conn = new SqlConnection(strConn))
                {
                    conn.Open();
                    
    string sql = string.Format("SELECT TOP 10 [Id],[FirstName] ,[LastName] ,[Weight] ,[Height] FROM Person(NOLOCK)");
                    
    //string sql = string.Format("SELECT TOP 10 [FirstName] ,[LastName] ,[Height] FROM Person(NOLOCK)");

                    SqlCommand cmd 
    = conn.CreateCommand();
                    cmd.CommandText 
    = sql;
                    SqlDataReader rdr 
    = cmd.ExecuteReader();
                    IList
    <Person> listPersons = new List<Person>();
                    
    while (rdr.Read())
                    {
                        Person model 
    = DynamicBuilder<Person>.CreateBuilder().Build(rdr);
                        listPersons.Add(model);
                    }
                    Console.WriteLine(
    string.Format("Theres are {0} people.", listPersons.Count));
                }
                Console.Read();
            }

    需要说明的是,生成实体的时候,while(rdr.Read())必须判断,原文里没有提及。

    三、改进ado.net快速上手里的DataReader转换成实体对象
    楼猪在旧文里曾经写了个数据转换类(ModelConverter),现在放弃反射的方式,采用Herbrandson介绍的动态代理实现数据实体的创建:

    代码
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Common;
    using System.Reflection;
    using System.Threading;

    namespace AdoNetDataAccess.Core.Obj2Model
    {
        
    using AdoNetDataAccess.Core.Contract;

        
    public sealed class ModelConverter
        {
            
    private static readonly object objSync = new object();

            
    #region query for list

            
    /// <summary>
            
    /// 查询数据表项并转换为对应实体
            
    /// </summary>
            
    /// <typeparam name="T"></typeparam>
            
    /// <param name="objType"></param>
            
    /// <param name="rdr"></param>
            
    /// <returns></returns>
            public static IList<T> QueryForList<T>(string sqlStr, CommandType cmdType, List<DbParameter> listParams, Type objType, IDbOperation dbOperation)
                
    where T : classnew()
            {
                IDataReader rdr 
    = dbOperation.ExecuteReader(sqlStr, cmdType, listParams);
                IList
    <T> listModels = new List<T>();
                
    try
                {
                    Monitor.Enter(objSync);
                    
    while (rdr.Read())
                    {
                        T model 
    = default(T);
                        model 
    = DynamicBuilder<T>.CreateBuilder(rdr).Build(rdr);//通过动态反射转换成实体
                        listModels.Add(model);
                    }

                    
    /*不用有人怨声载道的性能恶劣的反射了*/
                    
    //Hashtable ht = CreateHashColumnName(rdr);
                    
    //while (rdr.Read())
                    
    //{
                    
    //    Object obj = Activator.CreateInstance(objType);
                    
    //    PropertyInfo[] properties = objType.GetProperties();
                    
    //    foreach (PropertyInfo propInfo in properties)
                    
    //    {
                    
    //        string columnName = propInfo.Name.ToUpper();
                    
    //        if (ht.ContainsKey(columnName) == false)
                    
    //        {
                    
    //            continue;
                    
    //        }
                    
    //        int index = rdr.GetOrdinal(propInfo.Name);
                    
    //        object columnValue = rdr.GetValue(index);
                    
    //        if (columnValue != System.DBNull.Value)
                    
    //        {
                    
    //            SetValue(propInfo, obj, columnValue);
                    
    //        }
                    
    //    }
                    
    //    T model = default(T);
                    
    //    model = obj as T;
                    
    //    listModels.Add(model);
                    
    //}
                }
                
    finally
                {
                    rdr.Close();
                    rdr.Dispose();
                    Monitor.Exit(objSync);
                }
                
    return listModels;
            }

            
    #endregion

            
    #region query for dictionary

            
    /// <summary>
            
    /// 查询数据表项并转换为对应实体
            
    /// </summary>
            
    /// <typeparam name="K"></typeparam>
            
    /// <typeparam name="T"></typeparam>
            
    /// <param name="key">字典对应key列名</param>
            
    /// <param name="objType"></param>
            
    /// <param name="rdr"></param>
            
    /// <returns></returns>
            public static IDictionary<K, T> QueryForDictionary<K, T>(string key, string sqlStr, CommandType cmdType, List<DbParameter> listParams, Type objType, IDbOperation dbOperation)
                
    where T : classnew()
            {
                IDataReader rdr 
    = dbOperation.ExecuteReader(sqlStr, cmdType, listParams);
                IDictionary
    <K, T> dictModels = new Dictionary<K, T>();
                
    try
                {
                    Monitor.Enter(objSync);
                    
    while (rdr.Read())
                    {
                        T model 
    = default(T);
                        model 
    = DynamicBuilder<T>.CreateBuilder(rdr).Build(rdr);//通过动态反射转换成实体
                        
    //K objKey = GetModelKey<K, T>(key, model);
                        K objKey = BuildPrimaryKey<K, T>(key, rdr);
                        dictModels.Add(objKey, model);
                    }

                    
    /*不用有人怨声载道的性能恶劣的反射了*/
                    
    //Hashtable ht = CreateHashColumnName(rdr);
                    
    //while (rdr.Read())
                    
    //{
                    
    //    Object obj = Activator.CreateInstance(objType);
                    
    //    PropertyInfo[] properties = objType.GetProperties();
                    
    //    object dictKey = null;
                    
    //    foreach (PropertyInfo propInfo in properties)
                    
    //    {
                    
    //        string columnName = propInfo.Name.ToUpper();
                    
    //        if (ht.ContainsKey(columnName) == false)
                    
    //        {
                    
    //            continue;
                    
    //        }
                    
    //        int index = rdr.GetOrdinal(propInfo.Name);
                    
    //        object columnValue = rdr.GetValue(index);
                    
    //        if (columnValue != System.DBNull.Value)
                    
    //        {
                    
    //            SetValue(propInfo, obj, columnValue);
                    
    //            if (string.Compare(columnName, key.ToUpper()) == 0)
                    
    //            {
                    
    //                dictKey = columnValue;
                    
    //            }
                    
    //        }
                    
    //    }
                    
    //    T model = default(T);
                    
    //    model = obj as T;
                    
    //    K objKey = (K)dictKey;
                    
    //    dictModels.Add(objKey, model);
                    
    //}
                }
                
    finally
                {
                    rdr.Close();
                    rdr.Dispose();
                    Monitor.Exit(objSync);
                }
                
    return dictModels;
            }

            
    #endregion

            
    #region internal util

            
    //private static Hashtable CreateHashColumnName(IDataReader rdr)
            
    //{
            
    //    int len = rdr.FieldCount;
            
    //    Hashtable ht = new Hashtable(len);
            
    //    for (int i = 0; i < len; i++)
            
    //    {
            
    //        string columnName = rdr.GetName(i).ToUpper(); //不区分大小写
            
    //        string columnRealName = rdr.GetName(i);
            
    //        if (ht.ContainsKey(columnName) == false)
            
    //        {
            
    //            ht.Add(columnName, columnRealName);
            
    //        }
            
    //    }
            
    //    return ht;
            
    //}

            
    //private static void SetValue(PropertyInfo propInfo, Object obj, object objValue)
            
    //{
            
    //    try
            
    //    {
            
    //        propInfo.SetValue(obj, objValue, null);
            
    //    }
            
    //    catch
            
    //    {
            
    //        object realValue = null;
            
    //        try
            
    //        {
            
    //            realValue = Convert.ChangeType(objValue, propInfo.PropertyType);
            
    //            propInfo.SetValue(obj, realValue, null);
            
    //        }
            
    //        catch (Exception ex)
            
    //        {
            
    //            string err = ex.Message;
            
    //            //throw ex; //在数据库数据有不符合规范的情况下应该及时抛出异常
            
    //        }
            
    //    }
            
    //}

            
    //private static K GetModelKey<K, T>(string key, T model)
            
    //{
            
    //    object dictKey = null;
            
    //    PropertyInfo[] properties = model.GetType().GetProperties();
            
    //    foreach (PropertyInfo propInfo in properties)
            
    //    {
            
    //        if (string.Compare(propInfo.Name.ToUpper(), key.ToUpper()) == 0)
            
    //        {
            
    //            dictKey = propInfo.GetValue(model, null);
            
    //            break;
            
    //        }
            
    //    }
            
    //    K objKey = (K)dictKey;
            
    //    return objKey;
            
    //}

            
    private static K BuildPrimaryKey<K, T>(string key, IDataRecord dataRecord)
            {
                K dictKey 
    = default(K);
                
    for (int i = 0; i < dataRecord.FieldCount; i++)
                {
                    PropertyInfo propertyInfo 
    = typeof(T).GetProperty(dataRecord.GetName(i));
                    
    if (propertyInfo != null && string.Compare(key.ToUpper(), propertyInfo.Name.ToUpper()) == 0)
                    {
                        
    object value = dataRecord.GetValue(i);
                        dictKey 
    = (K)value;
                        
    break;
                    }
                }
                
    return dictKey;
            }

            
    #endregion
        }
    }


    DynamicBuilder类就是上面介绍的通过DynamicMethod和ILGenerator的方式。这样,QueryForList和QueryForDictionary方法的性能就得到了改善。
    需要说明的是,在实体转换的时候,QueryForDictionary方法的字典的key的获取是通过BuildPrimaryKey泛型方法,直接从IDataReader里取值,而没有通过emit生成动态代理获取(有心的童鞋可以小试牛刀,看看如何通过构建动态代理获取key),个人认为使用emit适可而止,没必要矫枉过正。
      
    先写到这里了。你还在纠结要不要学习emit? Yes,it works.

    demo下载:demo

  • 相关阅读:
    openwrt 相关文章
    负载均衡相关文章
    Today's Progress
    Rodrigues formula is beautiful, but uneven to sine and cosine. (zz Berkeley's Page)
    Camera Calibration in detail
    Fundamental Matrix in Epipolar
    Camera Calibration's fx and fy do Cares in SLAM
    FilterEngine::apply
    FilterEngine 类解析——OpenCV图像滤波核心引擎(zz)
    gaussBlur
  • 原文地址:https://www.cnblogs.com/jeffwongishandsome/p/1790057.html
Copyright © 2011-2022 走看看