【发布时间】:2015-03-30 06:01:33
【问题描述】:
我必须使用带有动态表列的实体框架(用户可以将列添加到我的表中),并且我需要有动态运行时类型的实体来映射我的表。
我的场景: 如果 DbContext 想要插入继承自 DynamicEntity 的静态实体(如 MyTableEntity),它将创建具有相同名称的具有“Dynamic_”前缀的实体(如 MyTableEntity)的动态类型,然后从生成的运行时类型创建实例,然后填充静态类型属性值 运行时对象的实例。最后插入方法将插入运行时对象。 每件事都很好并且工作。 但是因为 TypeBuilder 创建运行时类型,如果我调用 insert 两次或更多次,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();
【问题讨论】:
-
为什么需要删除旧类型?你遇到了什么错误?
-
当我想添加实体时,它会说:指定的架构无效。错误:CLR 类型到 EDM 类型的映射不明确,因为多个 CLR 类型与 EDM 类型“Dynamic_MyTableEntity”匹配。以前找到 CLR 类型“Dynamic_MyTableEntity”,新找到 CLR 类型“Dynamic_MyTableEntity”。
标签: c# entity-framework runtime il