1

我試圖建立一個動態的dbcontext,它不使用EF提供的DataAnnotation。動態調用EntityFramework EntityTypeConfiguration <>。HasKey

所以在我的覆蓋無效OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)我生成一個動態泛型類型具有所有的屬性和類型,所需要的關鍵:

Dictionary<Int32, PropertyInfo> dictIndex = new Dictionary<Int32, PropertyInfo>(); 
... 
Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(dictIndex.OrderBy(x => x.Key).Select(x => x.Value)); 
IEnumerable<MemberBinding> bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, Expression.Property(paramEx, dictIndex.Select(x => x.Value).FirstOrDefault(x => x.Name == p.Name)))).OfType<MemberBinding>(); 

ConstructorInfo ci = dynamicType.GetConstructor(Type.EmptyTypes); 
Expression selector = Expression.Lambda(Expression.MemberInit(Expression.New(ci), bindings), paramEx); 

var HasKey = config.GetType().GetMethod("HasKey").MakeGenericMethod(dynamicType); 
HasKey.Invoke(config, new[] { selector }); 

我發現LinqRuntimeTypeBuilder作爲回答另一個問題,並調整了代碼適合我的需要:

public static class LinqRuntimeTypeBuilder 
{ 
    private static AssemblyName assemblyName = Assembly.GetExecutingAssembly().GetName();//new AssemblyName() { Name = "DynamicLinqTypes" }; 
    private static ModuleBuilder moduleBuilder = null; 
    private static Dictionary<string, Type> builtTypes = new Dictionary<string, Type>(); 

    static LinqRuntimeTypeBuilder() 
    { 
     moduleBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(assemblyName.Name); 
    } 

    private static string GetTypeKey(Dictionary<string, Type> fields) 
    { 
     //TODO: optimize the type caching -- if fields are simply reordered, that doesn't mean that they're actually different types, so this needs to be smarter 
     string key = string.Empty; 
     key = "<>f__AnonymousType1`1"; 
     foreach (var field in fields) 
      key += field.Key + ";" + field.Value.Name + ";"; 

     return key; 
    } 

    public static Type GetDynamicType(Dictionary<string, Type> fields) 
    { 
     if (null == fields) 
      throw new ArgumentNullException("fields"); 
     if (0 == fields.Count) 
      throw new ArgumentOutOfRangeException("fields", "fields must have at least 1 field definition"); 

     try 
     { 
      Monitor.Enter(builtTypes); 
      string className = GetTypeKey(fields); 

      if (builtTypes.ContainsKey(className)) 
       return builtTypes[className]; 

      TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit | TypeAttributes.NotPublic,typeof(object)); 
      GenericTypeParameterBuilder[] gaBuilders = typeBuilder.DefineGenericParameters(fields.Select(x => "T" + x.Value.Name).ToArray()); 
      int i = 0; 
      foreach (var field in fields) 
       typeBuilder.DefineField(field.Key, gaBuilders[i++], FieldAttributes.Public); 

      builtTypes[className] = typeBuilder.CreateType().MakeGenericType(fields.Select(x => x.Value).ToArray()); 

      return builtTypes[className]; 
     } 
     catch (Exception ex) 
     { 
     } 
     finally 
     { 
      Monitor.Exit(builtTypes); 
     } 

     return null; 
    } 


    private static string GetTypeKey(IEnumerable<PropertyInfo> fields) 
    { 
     return GetTypeKey(fields.ToDictionary(f => f.Name, f => f.PropertyType)); 
    } 

    public static Type GetDynamicType(IEnumerable<PropertyInfo> fields) 
    { 
     return GetDynamicType(fields.ToDictionary(f => f.Name, f => f.PropertyType)); 
    } 
} 

但HasKey的調用拋出一個異常:

System.Reflection.TargetInvocationException: Ein Aufrufziel hat einen Ausnahmefehler verursacht. ---> System.InvalidOperationException: The properties expression `'Param_0 => new {LfdVtgNr = Param_0.LfdVtgNr}'` is not valid. The expression should represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'. When specifying multiple properties use an anonymous type: C#: 't => new { t.MyProperty1, t.MyProperty2 }' VB.Net: 'Function(t) New With { t.MyProperty1, t.MyProperty2 }'. 
    bei System.Data.Entity.Utilities.ExpressionExtensions.GetSimplePropertyAccessList(LambdaExpression propertyAccessExpression) 
    bei System.Data.Entity.ModelConfiguration.EntityTypeConfiguration`1.HasKey[TKey](Expression`1 keyExpression) 
    --- Ende der internen Ausnahmestapelüberwachung --- 
    bei System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) 
    bei System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) 

如果我使用modelBuilder.Configurations.HasKey('Param_0 => new {LfdVtgNr = Param_0.LfdVtgNr})進行靜態調用,但它不起作用。

當然,我可以用另一種方式做到這一點,但我想了解錯誤。

我將不勝感激任何幫助。

+0

BTW:有沒有EntityFramework的任何Symbol/sourceserver也是如此? –

回答

0

問題是您創建的表達式看起來與通常從返回匿名類型的lambda創建的表達式完全不同。

在你的情況,表達的身體看起來是這樣的:

  • MemberInit
      • 參數:空
      • 成員:空
    • 綁定:
      • 綁定:LfdVtgNr = Param_0.LfdVtgNr

但它應該是這樣的:

    • 參數:Param_0.LfdVtgNr
    • 成員:LfdVtgNr

(不要讓語法愚弄你,創建匿名類型是真正轉化爲只是一個構造函數調用,而不是MemberInit。)

但是,爲了使這項工作,生成的類型必須包含這樣的構造(儘管它實際上並不需要做任何事情)。另外,生成的類型不一定是通用的,因此您可以刪除與gaBuilders相關的所有代碼。

代碼的重要組成部分,現在這個樣子:

foreach (var field in fields) 
    typeBuilder.DefineField(field.Key, field.Value, FieldAttributes.Public); 

var parameters = fields.ToArray(); 

var ctor = typeBuilder.DefineConstructor(
    MethodAttributes.Public, CallingConventions.Standard, 
    parameters.Select(p => p.Value).ToArray()); 
var ctorIl = ctor.GetILGenerator(); 
ctorIl.Emit(OpCodes.Ret); 

for (int i = 0; i < parameters.Length; i++) 
{ 
    ctor.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].Key); 
} 

builtTypes[className] = typeBuilder.CreateType(); 

有了這個,你可以改變現在創建表達類似的代碼:

var ci = dynamicType.GetConstructors().Single(); 
var selector = 
    Expression.Lambda(
     Expression.New(
      ci, 
      ci.GetParameters() 
       .Select(
        p => Expression.Property(
         paramEx, dictIndex.Values.Single(x => x.Name == p.Name))), 
      ci.GetParameters().Select(p => dynamicType.GetField(p.Name))), 
     paramEx); 
+0

非常感謝。現在它工作正常。但爲什麼構造函數不必將參數分配給字段? –

+0

因爲構造函數從未實際調用。它僅在這裏用作檢查表達式的一部分。 – svick

相關問題