正在更新动态生成的程序集
本文关键字:程序集 动态 更新 | 更新日期: 2023-09-27 18:00:50
我有以下代码,它基于EdmModel动态生成程序集和类。这一切都很好,但我有点困惑如何在模型更改时更新部件。
以下是我目前的想法:
- 更新当前类,添加/删除属性。需要时添加新类
- 创建程序集的另一个版本,允许我同时运行这两个版本(这种方法更可取,因为它允许用户访问模型的不同版本,这意味着在生成新版本时仍可以使用当前版本(
可以用这种方式运行同一程序集的两个版本吗?是否可以更新已创建的类型?我这样做对吗?
public class DynamicAssembyGenerator
{
private IDictionary<string, AssemblyBuilder> _assemblyBuilders;
private IDictionary<string, ModuleBuilder> _moduleBuilders;
private IDictionary<string, TypeBuilder> _typeBuilders;
private IDictionary<string, EnumBuilder> _enumBuilders;
private IDictionary<string, Type> _types;
public void Create(IEdmModel model)
{
_assemblyBuilders = model.DeclaredNamespaces.ToDictionary(declaredNamespace => declaredNamespace,
CreateAssemblyBuilder);
_moduleBuilders = model.DeclaredNamespaces.ToDictionary(declaredNamespace => declaredNamespace,
declaredNamespace => CreateModuleBuilder(declaredNamespace, _assemblyBuilders[declaredNamespace]));
_typeBuilders = new Dictionary<string, TypeBuilder>();
_enumBuilders = new Dictionary<string, EnumBuilder>();
_types = new Dictionary<string, Type>();
foreach (
var edmSchemaElement in
model.SchemaElements.Where(s => s.SchemaElementKind == EdmSchemaElementKind.TypeDefinition))
{
if (_moduleBuilders.ContainsKey(edmSchemaElement.Namespace))
{
CreateType(model, edmSchemaElement.FullName(), _moduleBuilders[edmSchemaElement.Namespace]);
}
}
foreach (var assemblyBuilder in _assemblyBuilders.Values)
{
assemblyBuilder.Save(assemblyBuilder.GetName().Name + ".dll");
}
foreach (var value in _types.Values)
{
var edmType = model.FindDeclaredType(value.FullName);
model.SetAnnotationValue<ClrTypeAnnotation>(edmType, new ClrTypeAnnotation(value));
}
}
private void CreateType(IEdmModel model, string typeName, ModuleBuilder moduleBuilder)
{
if (_typeBuilders.ContainsKey(typeName) || _enumBuilders.ContainsKey(typeName))
{
return;
}
var structuredType = model.FindDeclaredType(typeName) as IEdmStructuredType;
if (structuredType != null)
{
var entityType = structuredType as IEdmEntityType;
var typeBuilder = CreateTypeBuilder(structuredType, model, moduleBuilder);
_typeBuilders.Add(typeName, typeBuilder);
var constructorBuilder = CreateConstructor(typeBuilder);
ILGenerator il = constructorBuilder.GetILGenerator();
foreach (var edmProperty in structuredType.DeclaredProperties)
{
CreateProperty(edmProperty, model, typeBuilder, il,
entityType != null && entityType.HasDeclaredKeyProperty(edmProperty));
}
il.Emit(OpCodes.Ret);
_types.Add(typeName, typeBuilder.CreateType());
return;
}
var enumType = model.FindDeclaredType(typeName) as IEdmEnumType;
if (enumType != null)
{
var enumBuilder = CreateEnumBuilder(typeName, moduleBuilder);
_enumBuilders.Add(typeName, enumBuilder);
foreach (var edmEnumMember in enumType.Members)
{
var value = edmEnumMember.Value as EdmIntegerConstant;
if (value != null)
{
CreateEnumValue(edmEnumMember.Name, Convert.ToInt32(value.Value), enumBuilder);
}
}
_types.Add(typeName, enumBuilder.CreateType());
return;
}
}
private AssemblyBuilder CreateAssemblyBuilder(string assemblyName)
{
var appDomain = AppDomain.CurrentDomain;
var assemblyBuilder = appDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave);
return assemblyBuilder;
}
private ModuleBuilder CreateModuleBuilder(string assemblyName, AssemblyBuilder assemblyBuilder)
{
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyName + ".dll");
return moduleBuilder;
}
private TypeBuilder CreateTypeBuilder(IEdmStructuredType type, IEdmModel model, ModuleBuilder moduleBuilder)
{
var baseType = typeof(DynamicEntityBase);
if (type.BaseType != null)
{
var propertyType = EdmLibHelpers.GetClrType(type.BaseType, model);
if (propertyType == null)
{
var schemaElement = model.FindDeclaredType(type.BaseType.FullTypeName());
CreateType(model, schemaElement.FullName(), _moduleBuilders[schemaElement.Namespace]);
baseType = _types[schemaElement.FullName()];
}
}
return moduleBuilder.DefineType(type.FullTypeName(), TypeAttributes.Class | TypeAttributes.Public, baseType);
}
private EnumBuilder CreateEnumBuilder(string typeName, ModuleBuilder moduleBuilder)
{
return moduleBuilder.DefineEnum(typeName, TypeAttributes.Public, typeof(int));
}
private void CreateEnumValue(string name, int value, EnumBuilder enumBuilder)
{
enumBuilder.DefineLiteral(name, value);
}
private void CreateProperty(IEdmProperty edmProperty, IEdmModel model, TypeBuilder typeBuilder, ILGenerator constructorIlGenerator, bool isKey)
{
var propertyType = GetPropertyType(edmProperty, model);
var propertyName = edmProperty.Name;
FieldBuilder fFirst;
PropertyBuilder pFirst = CreateProperty(propertyName, propertyType, typeBuilder, out fFirst);
SetDefaultValue(edmProperty, model, fFirst, constructorIlGenerator);
if (isKey)
{
SetPropertyCustomAttribute(pFirst, typeof(KeyAttribute));
}
if (edmProperty.VocabularyAnnotations(model).Any(v => v.Term.FullName() == CoreVocabularyConstants.Computed))
{
SetPropertyCustomAttribute(pFirst, typeof(ComputedAttribute));
}
if (edmProperty.PropertyKind == EdmPropertyKind.Navigation)
{
if (edmProperty.Type.IsCollection())
{
CreateProperty(propertyName + "Ids", typeof(IList<string>), typeBuilder, out fFirst);
var listType = typeof(List<string>);
constructorIlGenerator.Emit(OpCodes.Ldarg_0);
constructorIlGenerator.Emit(OpCodes.Newobj, listType.GetConstructor(new Type[0]));
constructorIlGenerator.Emit(OpCodes.Stfld, fFirst);
}
else
{
CreateProperty(propertyName + "Id", typeof(string), typeBuilder, out fFirst);
}
}
}
private static PropertyBuilder CreateProperty(string propertyName, Type propertyType, TypeBuilder typeBuilder, out FieldBuilder fieldBuilder)
{
fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder pFirst = typeBuilder.DefineProperty(propertyName,
PropertyAttributes.HasDefault, propertyType, null);
//Getter
MethodBuilder mFirstGet = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator firstGetIL = mFirstGet.GetILGenerator();
firstGetIL.Emit(OpCodes.Ldarg_0);
firstGetIL.Emit(OpCodes.Ldfld, fieldBuilder);
firstGetIL.Emit(OpCodes.Ret);
//Setter
MethodBuilder mFirstSet = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig, null, new Type[] { propertyType });
ILGenerator firstSetIL = mFirstSet.GetILGenerator();
firstSetIL.Emit(OpCodes.Ldarg_0);
firstSetIL.Emit(OpCodes.Ldarg_1);
firstSetIL.Emit(OpCodes.Stfld, fieldBuilder);
firstSetIL.Emit(OpCodes.Ret);
pFirst.SetGetMethod(mFirstGet);
pFirst.SetSetMethod(mFirstSet);
return pFirst;
}
private static void SetPropertyCustomAttribute(PropertyBuilder propertyBuilder, Type attributeType)
{
var attributeBuilder = new CustomAttributeBuilder(attributeType.GetConstructor(new Type[0]),
new object[0]);
propertyBuilder.SetCustomAttribute(attributeBuilder);
}
private Type GetPropertyType(IEdmProperty edmProperty, IEdmModel model)
{
var edmPropertyType = GetEdmType(edmProperty.Type);
var propertyType = EdmLibHelpers.GetClrType(edmPropertyType, model);
if (propertyType == null)
{
var schemaElement = model.FindDeclaredType(edmPropertyType.Definition.FullTypeName());
CreateType(model, schemaElement.FullName(), _moduleBuilders[schemaElement.Namespace]);
propertyType = _typeBuilders[schemaElement.FullName()];
}
if (edmProperty.Type.IsCollection())
{
var listType = typeof(IList<>);
propertyType = listType.MakeGenericType(propertyType);
}
return propertyType;
}
private IEdmTypeReference GetEdmType(IEdmTypeReference type)
{
if (type.IsCollection())
{
var collectionType = type.AsCollection();
return collectionType.CollectionDefinition().ElementType;
}
else
{
return type; ;
}
}
private void SetDefaultValue(IEdmProperty property, IEdmModel model, FieldBuilder builder, ILGenerator constructorIlGenerator)
{
if (!property.Type.IsNullable)
{
var propertyType = GetPropertyType(property, model);
if (property.Type.IsComplex())
{
constructorIlGenerator.Emit(OpCodes.Ldarg_0);
constructorIlGenerator.Emit(OpCodes.Newobj, propertyType.GetConstructor(new Type[0]));
constructorIlGenerator.Emit(OpCodes.Stfld, builder);
}
else if (property.Type.IsCollection())
{
var listType = typeof(List<>);
var collectionType = listType.MakeGenericType(propertyType.GetGenericArguments()[0]);
constructorIlGenerator.Emit(OpCodes.Ldarg_0);
constructorIlGenerator.Emit(OpCodes.Newobj, collectionType.GetConstructor(new Type[0]));
constructorIlGenerator.Emit(OpCodes.Stfld, builder);
}
}
}
private ConstructorBuilder CreateConstructor(TypeBuilder typeBuilder)
{
ConstructorBuilder constructor = typeBuilder.DefineConstructor(
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.RTSpecialName,
CallingConventions.Standard,
new Type[0]);
return constructor;
}
}
- 更新当前类,添加/删除属性
不可能。一旦调用TypeBuilder.CreateType()
,就无法删除/修改您创建的类型。
- 需要时添加新类
可能没有问题。您可以使用您在AssemblyBuilder
中定义的类型,而无需保存,因此可以添加新类型。
- 创建程序集的另一个版本,允许我同时运行这两个版本
这里没问题。
- 可以用这种方式运行同一程序集的两个版本吗
对于.NET来说,这不是同一个程序集…它们有什么共同点?你的抽象概念"他们是一样的"?但是.NET无法查看你的大脑:-(