Dynamic Entity EF5 Create And *REMOVE OR RECREATE* TypeBuilder

мне нужно использовать структуру сущностей с динамическими столбцами таблицы (этот пользователь может добавить столбец в мою таблицу), и мне нужно иметь динамическую сущность типа времени выполнения для сопоставления моей таблицы.

мой сценарий: если DbContext хочет вставить статическую сущность (например, MyTableEntity), унаследованную от DynamicEntity, он создаст динамический тип с тем же именем сущности (например, MyTableEntity) с префиксом «Dynamic_», после этого создаст экземпляр из сгенерированного типа среды выполнения, а затем заполнит значение свойств статического типа для экземпляра объекта среды выполнения. наконец, метод вставки вставит объект времени выполнения. все хорошо и работает. но поскольку TypeBuilder создает тип времени выполнения, и если я вызываю вставку два или более раз, TypeBuilder создаст новый тип с тем же именем, например «Dynamic_MyTableEntity», и DbContext не может распознать класс ведьм, который должен быть вставлен, и это естественно.

мой вопрос: как я могу удалить старый тип, который я создал с помощью TypeBuilder, или обновить или обновить старый тип, например, сначала удалить все свойства, а затем снова добавить все свойства.

я создаю класс, унаследованный от DynamicObject, и я унаследую свои динамические объекты от класса DynamicEntity.

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

person Emran Sadeghi    schedule 30.03.2015    source источник
comment
Зачем нужно удалять старый тип? Какую ошибку вы получаете?   -  person svick    schedule 30.03.2015
comment
когда я хочу добавить объект, он скажет: указанная схема недействительна. Ошибки: сопоставление типа CLR с типом EDM неоднозначно, поскольку несколько типов CLR соответствуют типу EDM «Dynamic_MyTableEntity». Ранее найденный тип CLR «Dynamic_MyTableEntity», недавно обнаруженный тип CLR «Dynamic_MyTableEntity».   -  person Emran Sadeghi    schedule 31.03.2015


Ответы (1)


наконец, я изменил свой 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 работает нормально...

person Emran Sadeghi    schedule 04.04.2015