正在更新动态生成的程序集

本文关键字:程序集 动态 更新 | 更新日期: 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无法查看你的大脑:-(