2009-08-13 19 views
5

Nesnenin özelliklerini yansıma kullanmadan DynamicBuilder on CodeProject benzeri bir şekilde doldurmak istiyorum. CodeProject örneği, bir DataReader veya DataRecord kullanarak varlıkların doldurulması için uyarlanmıştır. Bunu iyi etki için birkaç DAL'de kullanıyorum. Şimdi bir sözlük veya başka bir veri agnostik nesneyi kullanmak için değiştirmek istiyorum, böylece ben DAL kodda kullanamıyorum - şu anda yansıma kullandığım yerler. OpCodes ve IL hakkında neredeyse hiçbir şey bilmiyorum. Sadece iyi çalıştığını ve yansımadan daha hızlı olduğunu biliyorum.Dinamik nesne özellik popülatörü (yansıma olmadan)

CodeProject örneğini değiştirmeye çalıştım ve IL ile ilgili bilgisizliğim nedeniyle, iki satırda sıkışmış durumdayım. Bunlardan

  • Bir DBNULLs ilgilenir ve ben sadece onu kaybedebilir eminim, ama hatları önceki ve onu ilişkilidir ve gitmek için de bunlardan hangisinin gereken aşağıdaki eğer bilmiyorum.
  • Diğeri, daha önce veri kaydının değerini çeken ve şimdi onu sözlükten çıkarmak zorunda olan bence. Sanırım "getValueMethod" özelliğini "property.Value" ile değiştirebiliyorum ama emin değilim.

Bu kediyi de cildin alternatif/daha iyi yollarına açığım.

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

DÜZENLEME: kod (HyperDescriptor ile) Marc Gravell en PropertyDescriptor uygulanmasını kullanma

basitleştirilmiş İşte

bugüne kadar kod (yorumlandı hatlar ı şaşırıp olanlar) yüz kat. Şimdi aşağıdaki test var: Her iki TypeDescriptor.GetProperties için performans değerlendirmeleri esas

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

herhangi bir yorum() & PropertyDescriptor.SetValue() bekliyoruz ...

+0

(yorum yapmak cevap) –

cevap

9

Düzenleme: tüm bu temelde neyi şık olduğunu yapar - ama dapper çok daha iyi optimize edilmiştir. Bu cevabı bugün yazıyor olsaydım, basitçe okurdu: "dapper kullan". Eğer derece "yukarı" IL üzerinde değilseniz


, size IL hızını ve yansıma kolaylık olsun alternatifler vardır.

İlk örnek:

HyperDescriptor - (HyperDescriptor etkinleştirmek için artı bir-liner) sizin için IL ile ilgilenen özel bir PropertyDescriptor modeli, bu nedenle tek şey gibi kodudur kullanır:

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

İkinci örnek:

LINQ ifadeleri - oldukça uzun, ama bunu usenet üzerinde tartıştım (ve yukarısı ortaya çıkıyor) - bkz. this archive.

+0

Parlak!HyperDescriptor'u neden etkinleştirmem gerektiğini açıklayabilir misiniz? Kod örneklerinizi veri okuyucudan kurtulmak için değiştirdim ve HyperDescriptor ile birlikte ve test yapmamaya karar verdim. Ben sadece özellikleri halka açık bir erişimci ile ayarlamak istediğimden, HyperDescriptor'a ihtiyacım yok gibi görünüyor. - ya da bir şey özledim mi? – grenade

+1

HyperDescriptor, hızlı hale getiren sihirli tutkaldır. Aksi halde, esas olarak yansıma sarılır. HyperDescriptor, özel IL ** yazar, bu yüzden ** 'ye sahip olmamanız ve * normal "PropertyDescriptor" modeli gibi * tam olarak * görünmesini sağlar. Hangisi güzel. –

+0

dohhhhhhhhhhhhhhhhhhhhh! – grenade

0

Evet, böyle bir kod kullanabilirsiniz:

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

.... ...

İlgili konular