2014-09-28 5 views
3

У меня есть код, который использует IlGenerator.Emit для создания и заполнения общего объекта с помощью datareader. Он отлично работает, но мне нужно расширить его, чтобы заполнить простые дочерние объекты, когда имя поля базы данных содержит символ подчеркивания.Настройка объекта sub с использованием ILGenerator.Emit

Например, поле базы данных с именем «Address_Line1» должно заполнить свойство Line1, которое является свойством свойства Address на Entity. В C# код, который в основном ...

Entity.Address.Line1 = "value from reader"; 

Я пытался писать C# код и использовать ILSpy, чтобы попытаться определить код IL я должен писать, но я получаю ошибку памяти и т.д.

ниже код включает в себя текущий действующий код IL, и я включил мою попытку кода с комментариями. Может кто-нибудь мне помочь?

public static DynamicBuilder<T> CreateBuilder(IDataRecord reader) 
{ 
    var result = new DynamicBuilder<T>(); 
    var method = new DynamicMethod("DynamicCreate", typeof(T), new Type[] { typeof(IDataReader) }, typeof(T), true); 

    var generator = method.GetILGenerator(); 

    generator.DeclareLocal(typeof(T)); 
    generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes)); 
    generator.Emit(OpCodes.Stloc_0); 

    var getValue = reader.GetType().GetMethod("get_Item", new Type[] { typeof(int) }); 

    for (int i = 0; i < reader.FieldCount; i++) 
    { 
     var name = reader.GetName(i).Split('_'); // MY CODE 
     var propertyInfo = typeof(T).GetProperty(name[0]); 

     if (propertyInfo != null && propertyInfo.GetSetMethod() != null) 
     { 
      var endIfLabel = generator.DefineLabel(); 

      generator.Emit(OpCodes.Ldarg_0); 
      generator.Emit(OpCodes.Ldc_I4, i); 
      generator.Emit(OpCodes.Callvirt, typeof(IDataRecord).GetMethod("IsDBNull")); 
      generator.Emit(OpCodes.Brtrue, endIfLabel); 

      generator.Emit(OpCodes.Ldloc_0); 
      generator.Emit(OpCodes.Ldarg_0); 
      generator.Emit(OpCodes.Ldc_I4, i); 
      generator.Emit(OpCodes.Callvirt, getValue); 

      if (propertyInfo.PropertyType.Name.ToLower().Contains("nullable")) 
       generator.Emit(OpCodes.Unbox_Any, GetNullableType(reader.GetFieldType(i))); 
      else 
       generator.Emit(OpCodes.Unbox_Any, reader.GetFieldType(i)); 

      // START MY CODE TO GET THE SUB PROPERTY 
      if (name.Length > 1) 
      { 
       generator.Emit(OpCodes.Callvirt, propertyInfo.GetGetMethod()); 
       propertyInfo = propertyInfo.PropertyType.GetProperty(name[1]); 
      } 
      // END MY CODE 

      generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod()); 
      generator.MarkLabel(endIfLabel); 
     } 
    } 

    generator.Emit(OpCodes.Ldloc_0); 
    generator.Emit(OpCodes.Ret); 

    result.handler = (Load)method.CreateDelegate(typeof(Load)); 
    return result; 
} 
+0

Вы пробовали испуская этот код сборки, а затем работает peverify на нем? – svick

+0

Выполнение peverify против dll возвращает все классы и методы в my.dll проверено. – WDuffy

+0

Не против DLL, которая содержит этот код, против испускаемого кода. – svick

ответ

3

код так:

static Entity DynamicCreate(IDataReader reader) 
{ 
    var entity = new Entity(); 
    entity.Property = (int)reader[0]; 
    return entity; 
} 

компилируется в IL, который выглядит точно так же, как код вы излучающей (несущественные детали опущены):

ldloc.0  // entity 
ldarg.0  // reader 
ldc.i4.0  
callvirt System.Data.IDataRecord.get_Item 
unbox.any System.Int32 
callvirt UserQuery+Entity.set_Property 

Но если добавить, что второй доступ к собственности:

static Entity DynamicCreate(IDataReader reader) 
{ 
    var entity = new Entity(); 
    entity.SubEntity.Property = (int)reader[0]; 
    return entity; 
} 

Тогда IL выглядит следующим образом:

ldloc.0  // entity 
callvirt UserQuery+Entity.get_SubEntity 
ldarg.0  // reader 
ldc.i4.0  
callvirt System.Data.IDataRecord.get_Item 
unbox.any System.Int32 
callvirt UserQuery+SubEntity.set_Property 

Обратите внимание, что вызов get_SubEntity между ldloc.0 и ldarg.0, а не прямо перед set_Property, как в коде, так что вы должны переместить его туда в вашем коде тоже.

Причина, по которой ваш код не работает, заключается в том, что IL является языком на основе стека: когда вы вызываете метод без параметров (например, свойство getter), объект в верхней части стека (который здесь является результатом unbox.any) будет использоваться как его this, который здесь вам не нужен. В принципе, ваш код пытается сделать что-то вроде:

entity.Property = ((int)reader[0]).SubEntity; 
+1

svick, который исправил его :) Большое спасибо за подробный ответ! – WDuffy

-1

Я предлагаю не использовать IL-код/​​испускают совсем так как трудно строить выражения с ним. Вместо этого попробуйте использовать новый Roslyn для генерации делегатов.

Вот некоторые примеры: https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples

 Смежные вопросы

  • Нет связанных вопросов^_^