2009-08-13 13 views
5

Quiero rellenar las propiedades de un objeto sin utilizar el reflejo de manera similar al DynamicBuilder on CodeProject. El ejemplo de CodeProject está diseñado para poblar entidades usando un DataReader o DataRecord. Lo uso en varios DAL con buenos resultados. Ahora quiero modificarlo para usar un diccionario u otro objeto agnóstico de datos para poder usarlo en un código que no sea DAL - lugares en los que actualmente uso el reflejo. No sé casi nada sobre OpCodes e IL. Solo sé que funciona bien y es más rápido que la reflexión.Populador de propiedades de objetos dinámicos (sin reflejo)

He intentado modificar el ejemplo de CodeProject y debido a mi ignorancia con IL, me he atascado en dos líneas.

  • Uno de ellos tiene que ver con dbnulls y estoy bastante seguro de que sólo puede perderlo, pero no sé si las líneas anteriores y posteriores que están relacionados y cuáles de ellos también tendrá que ir.
  • El otro, creo, es el que sacó el valor del registro de datos antes y ahora necesita sacarlo del diccionario. Creo que puedo reemplazar el "getValueMethod" con mi "property.Value" pero no estoy seguro.

Estoy abierto a formas alternativas/mejores de desollar a este gato también.

Aquí está el código hasta el momento (las líneas comentadas son las que yo estoy atascado en):

using System; 
using System.Collections.Generic; 
using System.Reflection; 
using System.Reflection.Emit; 

public class Populator<T> 
{ 
    private delegate T Load(Dictionary<string, object> properties); 
    private Load _handler; 
    private Populator() { } 
    public T Build(Dictionary<string, object> properties) 
    { 
     return _handler(properties); 
    } 
    public static Populator<T> CreateBuilder(Dictionary<string, object> properties) 
    { 
     //private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new [] { typeof(int) }); 
     //private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new [] { typeof(int) }); 
     Populator<T> dynamicBuilder = new Populator<T>(); 
     DynamicMethod method = new DynamicMethod("Create", typeof(T), new[] { typeof(Dictionary<string, object>) }, typeof(T), true); 
     ILGenerator generator = method.GetILGenerator(); 
     LocalBuilder result = generator.DeclareLocal(typeof(T)); 
     generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes)); 
     generator.Emit(OpCodes.Stloc, result); 
     int i = 0; 
     foreach (var property in properties) 
     { 
      PropertyInfo propertyInfo = typeof(T).GetProperty(property.Key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy | BindingFlags.Default); 
      Label endIfLabel = generator.DefineLabel(); 

      if (propertyInfo != null && propertyInfo.GetSetMethod() != null) 
      { 
       generator.Emit(OpCodes.Ldarg_0); 
       generator.Emit(OpCodes.Ldc_I4, i); 
       //generator.Emit(OpCodes.Callvirt, isDBNullMethod); 
       generator.Emit(OpCodes.Brtrue, endIfLabel); 

       generator.Emit(OpCodes.Ldloc, result); 
       generator.Emit(OpCodes.Ldarg_0); 
       generator.Emit(OpCodes.Ldc_I4, i); 
       //generator.Emit(OpCodes.Callvirt, getValueMethod); 

       generator.Emit(OpCodes.Unbox_Any, property.Value.GetType()); 
       generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod()); 
       generator.MarkLabel(endIfLabel); 
      } 
      i++; 
     } 

     generator.Emit(OpCodes.Ldloc, result); 
     generator.Emit(OpCodes.Ret); 
     dynamicBuilder._handler = (Load)method.CreateDelegate(typeof(Load)); 
     return dynamicBuilder; 
    } 
} 

EDIT:

Usando aplicación PropertyDescriptor de Marc Gravell (con HyperDescriptor) el código se simplifica cien veces más Ahora tengo la siguiente prueba:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using Hyper.ComponentModel; 

namespace Test 
{ 
    class Person 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
    } 

    class Program 
    { 
     static void Main() 
     { 
      HyperTypeDescriptionProvider.Add(typeof(Person)); 
      var properties = new Dictionary<string, object> { { "Id", 10 }, { "Name", "Fred Flintstone" } }; 
      Person person = new Person(); 
      DynamicUpdate(person, properties); 
      Console.WriteLine("Id: {0}; Name: {1}", person.Id, person.Name); 
      Console.ReadKey(); 
     } 

     public static void DynamicUpdate<T>(T entity, Dictionary<string, object> properties) 
     { 
      foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(T))) 
       if (properties.ContainsKey(propertyDescriptor.Name)) 
        propertyDescriptor.SetValue(entity, properties[propertyDescriptor.Name]); 
     } 
    } 
} 

Cualquier comentario sobre las consideraciones de rendimiento para ambos TypeDescriptor.GetProperties() & PropertyDescriptor.SetValue() son bienvenidos ...

+0

(Respondió el comentario) –

Respuesta

9

Editar: todo esto es básicamente lo atildado lo hace, pero dapper está mucho más optimizado. Si escribiera esta respuesta hoy, se leería simplemente: "use dapper".


Si no está enormemente "arriba" en IL, existen alternativas que se obtiene la velocidad de la IL y la conveniencia de la reflexión.

Primer ejemplo:

HyperDescriptor - utiliza un modelo personalizado PropertyDescriptor que se ocupa de la IL para usted, por lo que todo lo que tiene es un código como (más el de una sola línea para permitir HyperDescriptor):

public static IEnumerable<T> Read<T>(IDataReader reader) where T : class, new() 
{ 
    PropertyDescriptorCollection props = 
     TypeDescriptor.GetProperties(typeof(T)); 

    PropertyDescriptor[] propArray = new PropertyDescriptor[reader.FieldCount]; 
    for (int i = 0; i < propArray.Length; i++) 
    { 
     propArray[i] = props[reader.GetName(i)]; 
    } 
    while(reader.Read()) { 
     T item = new T(); 
     for (int i = 0; i < propArray.Length; i++) 
     { 
      object value = reader.IsDBNull(i) ? null : reader[i]; 
      propArray[i].SetValue(item, value); 
     } 
     yield return item; 
    } 
} 

Segundo ejemplo:

Expresiones de LINQ: bastante largas, pero ya he hablado de esto (y de lo anterior, resulta) en usenet: consulte this archive.

+0

Brilliant!¿Puede explicar por qué necesito habilitar HyperDescriptor? Modifiqué aquí su ejemplo de código para deshacerme del lector de datos y me he burlado de las pruebas con y sin HyperDescriptor. Como, solo quiero establecer propiedades con un acceso público, parece que no necesito HyperDescriptor. - o me he perdido algo? – grenade

+1

HyperDescriptor es el pegamento mágico que lo hace rápido. De lo contrario, es esencialmente reflexión envuelta. HyperDescriptor escribe IL ** personalizado para que no tenga que **, y lo hace ver * exactamente * como el modelo 'PropertyDescriptor' normal. Lo cual es bueno. –

+0

dohhhhhhhhhhhhhhhhhhhhh! – grenade

0

Sí, puede utilizar código como el siguiente:

for (int i = 0; i < dataRecord.FieldCount; i++) 
       { 

        PropertyInfo propertyInfo = t.GetProperty(dataRecord.GetName(i)); 
        LocalBuilder il_P = generator.DeclareLocal(typeof(PropertyInfo)); 

        Label endIfLabel = generator.DefineLabel(); 

.... ...

Cuestiones relacionadas