2009-08-13 9 views
5

Ich möchte die Eigenschaften eines Objekts ohne Verwendung von Reflektion auf ähnliche Weise wie DynamicBuilder on CodeProject auffüllen. Das CodeProject-Beispiel ist auf das Füllen von Entitäten mit einem DataReader oder DataRecord zugeschnitten. Ich benutze dies in mehreren DALs mit gutem Erfolg. Jetzt möchte ich es ändern, um ein Wörterbuch oder ein anderes datenunabhängiges Objekt zu verwenden, so dass ich es in Nicht-DAL-Code verwenden kann - Orte, an denen ich derzeit Reflektion verwende. Ich weiß fast nichts über OpCodes und IL. Ich weiß einfach, dass es gut funktioniert und schneller ist als Nachdenken.Dynamischer Objekteigenschaften-Populator (ohne Reflektion)

Ich habe versucht, das Codeproject Beispiel und wegen meiner Unwissenheit mit IL zu ändern, ich habe auf zwei Zeilen stecken geblieben.

  • Einer von ihnen beschäftigt sich mit dbnulls und ich bin mir ziemlich sicher, dass ich es nur verlieren kann, aber ich weiß nicht, ob die Zeilen davor und danach verbunden sind und welche von ihnen werden auch gehen müssen.
  • Die andere, glaube ich, ist derjenige, der den Wert aus dem Datarecord vor und jetzt ist es aus dem Wörterbuch zu ziehen braucht gezogen. Ich denke, ich kann die "getValueMethod" mit meinem "property.Value" ersetzen, aber ich bin mir nicht sicher.

Ich bin offen für alternative/bessere Möglichkeiten, diese Katze auch zu enthäuten.

Hier ist der Code so weit (die auf Kommentar Linien sind die, die ich auf bin stecken):

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:

Mit Marc GRA der PropertyDescriptor Implementierung (mit HyperDescriptor) den Code vereinfacht ein Hundertfaches. Ich habe jetzt den folgenden Test:

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]); 
     } 
    } 
} 

Alle Kommentare zu Leistungsinformationen für beide TypeDescriptor.GetProperties() & PropertyDescriptor.SetValue() sind willkommen ...

+0

(antwortete Kommentar) –

Antwort

9

Edit: all dies ist im Grunde, was dapper ist - aber Dapper ist viel mehr optimiert. Wenn ich heute diese Antwort schreiben würde, würde es einfach heißen: "benutze dapper".


Wenn Sie nicht sehr groß „up“ auf IL sind, gibt es Alternativen, die die Geschwindigkeit von IL und die Bequemlichkeit der Reflexion erhalten.

Erstes Beispiel:

HyperDescriptor - verwendet ein benutzerdefiniertes PropertyDescriptor Modell, das für Sie mit der IL beschäftigt, so alles, was Sie haben, ist Code wie (plus die Einzeiler HyperDescriptor zu ermöglichen):

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; 
    } 
} 

Zweites Beispiel:

LINQ Ausdrücke - ziemlich lang, aber ich habe dies diskutiert (und das oben, es stellt sich heraus) auf Usenet - siehe this archive.

+0

Brilliant!Können Sie näher erläutern, warum ich HyperDescriptor aktivieren muss? Ich habe das Codebeispiel hier geändert, um den Datenreader loszuwerden, und ich habe Tests mit und ohne HyperDescriptor getaktet. Da ich nur Eigenschaften mit einem öffentlichen Accessor festlegen möchte, scheint es, dass ich HyperDescriptor nicht benötige. - Oder habe ich etwas verpasst? – grenade

+1

HyperDescriptor ist der magische Klebstoff, der es schnell macht. Ansonsten ist es im Wesentlichen Reflektion eingepackt. HyperDescriptor schreibt benutzerdefinierte IL **, also müssen Sie nicht **, und es sieht * genau * wie das normale 'PropertyDescriptor'-Modell. Was nett ist. –

+0

dohhhhhhhhhhhhhhhhhhhhh! – grenade

0

Ja, können Sie folgenden Code verwenden:

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(); 

.... ...