动态实体 EF5 创建并*删除或重新创建* TypeBuilder

Dynamic Entity EF5 Create And *REMOVE OR RECREATE* TypeBuilder

我必须将 entity framework 与动态 table 列一起使用(该用户可以将列添加到我的 table),并且我需要动态 运行-时间键入实体以映射我的 table.

我的场景: 如果 DbContext 想要插入从 DynamicEntity 继承的静态实体(如 MyTableEntity),它将创建具有相同名称的实体(如 MyTableEntity)的动态类型,前缀为 "Dynamic_",然后从生成的 运行time 创建实例类型,然后将静态类型属性值填充到 运行时间对象的实例。最后插入方法将插入 运行time 对象。 一切都很好,工作。 但是因为 TypeBuilder 创建 运行time 类型并且如果我调用插入两次或更多次,TypeBuilder 将创建具有相同名称的新类型,如 "Dynamic_MyTableEntity" 并且 DbContext 无法识别 class 必须插入自然。

我的问题: 我如何删除由 TypeBuilder 创建的旧类型或更新或更新旧类型,例如首先删除所有属性然后再次添加所有属性。

我创建了从 DynamicObject 继承的 class,我将从 DynamicEntity class.

继承我的动态实体
public class DynamicEntity : System.Dynamic.DynamicObject {
    //Runtime Type Prefix 
    public const string DynamicTypePrefix = "Dynamic_";

    //Dictionary Key = PropertyName, Value = Value Of Property
    private Dictionary<string, object> properties = new Dictionary<string, object>();

    //Dictionary Key = typeof static type, Value = Dynamic Type, Corresponding static type
    private static Dictionary<Type, Type> staticType_DynamicType = new Dictionary<Type, Type>();

    private static Assembly currentAssembly;
    private Assembly CurrentAssembly {
        get {
            if (currentAssembly == null) {
                currentAssembly = Assembly.GetAssembly(type);
            }
            return currentAssembly;
        }
    }

    //Generate dynamic type from static type, and Cache it to staticType_DynamicType for later use, and return
    public Type GetDynamicType() {
        Type dynamicType;

        if (!staticType_DynamicType.TryGetValue(type, out dynamicType)) {
            TypeBuilder typeBuilder = CreateTypeBuilder(CurrentAssembly.FullName, CurrentAssembly.GetLoadedModules()[0].Name, DynamicTypePrefix + type.Name);

            foreach (var item in properties.Where(q => q.Value != null).Select(q => new { Name = q.Key, Type = q.Value.GetType() })) {
                CreateAutoImplementedProperty(typeBuilder, item.Name, item.Type);
            }

            dynamicType = typeBuilder.CreateType();
            staticType_DynamicType[type] = dynamicType;
        }

        return dynamicType;
    }

    //Create TypeBuilder
    private TypeBuilder CreateTypeBuilder(string assemblyName, string moduleName, string typeName) {


        TypeBuilder typeBuilder = AppDomain
            .CurrentDomain
            .DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run)
            .DefineDynamicModule(moduleName)
            .DefineType(typeName, TypeAttributes.Public, type);
        typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
        return typeBuilder;
    }

    //Create Property for TypeBuilder
    private static void CreateAutoImplementedProperty(
        TypeBuilder builder, string propertyName, Type propertyType) {
        const string PrivateFieldPrefix = "m_";
        const string GetterPrefix = "get_";
        const string SetterPrefix = "set_";

        // Generate the field.
        FieldBuilder fieldBuilder = builder.DefineField(
            string.Concat(PrivateFieldPrefix, propertyName),
                          propertyType, FieldAttributes.Private);

        // Generate the property
        PropertyBuilder propertyBuilder = builder.DefineProperty(
            propertyName, System.Reflection.PropertyAttributes.HasDefault, propertyType, null);

        // Property getter and setter attributes.
        MethodAttributes propertyMethodAttributes =
            MethodAttributes.Public | MethodAttributes.SpecialName |
            MethodAttributes.HideBySig;

        // Define the getter method.
        MethodBuilder getterMethod = builder.DefineMethod(
            string.Concat(GetterPrefix, propertyName),
            propertyMethodAttributes, propertyType, Type.EmptyTypes);

        // Emit the IL code.
        // ldarg.0
        // ldfld,_field
        // ret
        ILGenerator getterILCode = getterMethod.GetILGenerator();
        getterILCode.Emit(OpCodes.Ldarg_0);
        getterILCode.Emit(OpCodes.Ldfld, fieldBuilder);
        getterILCode.Emit(OpCodes.Ret);

        // Define the setter method.
        MethodBuilder setterMethod = builder.DefineMethod(
            string.Concat(SetterPrefix, propertyName),
            propertyMethodAttributes, null, new Type[] { propertyType });

        // Emit the IL code.
        // ldarg.0
        // ldarg.1
        // stfld,_field
        // ret
        ILGenerator setterILCode = setterMethod.GetILGenerator();
        setterILCode.Emit(OpCodes.Ldarg_0);
        setterILCode.Emit(OpCodes.Ldarg_1);
        setterILCode.Emit(OpCodes.Stfld, fieldBuilder);
        setterILCode.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(getterMethod);
        propertyBuilder.SetSetMethod(setterMethod);
    }

    //Create new instance from runtime type and initialize properties with static type
    public object CreateDynamicInstance() {
        Type dynamicType = GetDynamicType();
        object instance = Activator.CreateInstance(dynamicType);
        foreach (var item in type.GetProperties()) {
            dynamicType.GetProperty(item.Name).SetValue(instance, item.GetValue(this, null), null);
        }

        foreach (var item in properties) {
            dynamicType.GetProperty(item.Key).SetValue(instance, item.Value, null);
        }

        return instance;
    }

    //Static type
    private Type type;
    public DynamicEntity() {
        type = this.GetType();
    }

    //Set Dynamic Property to static type
    public void SetMember(string name, object value) {
        lock (this) {
            properties[name] = value;
            if (staticType_DynamicType.ContainsKey(type)) {
                staticType_DynamicType.Remove(type);
            }
        }
    }

    public override bool TrySetMember(SetMemberBinder binder, object value) {
        SetMember(binder.Name, value);

        return true;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        lock (this) {
            result = properties[binder.Name];
        }
        return true;
    }
}

对我的实体进行采样:

public class MyTableEntity : DynamicEntity {
    [Key]
    public int ID { get; set; }
}

示例使用:

dynamic entity = new MyTableEntity();
entity.SetMember("MyNewColumn", "this is value of column"); //or entity.MyNewColumn = "this is value of column";

myDbContext.Set(entity.GetDynamicType()).Add(entity.CreateDynamicInstance());
myDbContext.SaveChanges();

最后我更改了我的 dbcontext,我的问题解决了

public class HREntities : DbContext {
    public HREntities(string connectionString)
        : base(connectionString) {
    }


    public HREntities(string connectionString, Type entityType)
        : base(connectionString, GenerateDbCompiledModel(connectionString, entityType)) {

    }

    private static DbCompiledModel GenerateDbCompiledModel(string connectionString, Type entityType) {
        string tableName;

        if (typeof(DynamicEntity).IsAssignableFrom(entityType)) {
            tableName = entityType.Name.Substring((DynamicEntity.DynamicTypePrefix + "tbl").Length);
        }
        else {
            tableName = entityType.Name.Substring("tbl".Length);
        }

        DbModelBuilder dbModelBuilder = new DbModelBuilder(DbModelBuilderVersion.Latest);
        var entityMethod = dbModelBuilder.GetType().GetMethod("Entity").MakeGenericMethod(entityType);
        var entityTypeConfiguration = entityMethod.Invoke(dbModelBuilder, new object[0]);
        entityTypeConfiguration.GetType().GetMethod("ToTable", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string) }, null).Invoke(entityTypeConfiguration, new object[] { tableName });

        return dbModelBuilder.Build(new SqlConnection(connectionString)).Compile();
    }
}

每次当我想创建 HREntities 实例时,我都会传递我的动态类型并且我的 dbcontext 工作正常...