设置 TypeBuilder.CreateType() 的位置?
Set location of TypeBuilder.CreateType()?
我正在 .NET 4.7.2 的运行时期间使用 TypeBuilder 创建 c# class。问题是该类型的 DLL 存储在当前应用程序的根文件夹中。问题是用户通常没有对本地根文件夹的写入权限。
那么如何设置程序集的保存和加载位置呢?什么用户文件夹适合这个?
当前代码:
private static Type CreateRaportType(List<PropertieInformation> propertieList, string className)
{
AssemblyName assemblyName;
AssemblyBuilder assemblyBuilder;
ModuleBuilder module;
TypeBuilder typeBuilder;
FieldBuilder field;
PropertyBuilder property;
MethodAttributes GetSetAttr;
MethodBuilder currGetPropMthdBldr;
MethodBuilder currSetPropMthdBldr;
ILGenerator currGetIL;
ILGenerator currSetIL;
Type caType;
CustomAttributeBuilder caBuilder;
List<Object> objList = new List<object>();
assemblyName = new AssemblyName();
assemblyName.Name = "ReportAssembly";
assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
module = assemblyBuilder.DefineDynamicModule("ReportModule");
typeBuilder = module.DefineType(className, TypeAttributes.Public | TypeAttributes.Class, typeof(GeneratedClassBase));
foreach (PropertieInformation propertieInfo in propertieList)
{
field = typeBuilder.DefineField("_" + propertieInfo.PropertieName, propertieInfo.PropertieType, FieldAttributes.Private);
property = typeBuilder.DefineProperty(propertieInfo.PropertieName, PropertyAttributes.None, propertieInfo.PropertieType, new Type[] { propertieInfo.PropertieType });
GetSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig;
currGetPropMthdBldr = typeBuilder.DefineMethod("get_value", GetSetAttr, propertieInfo.PropertieType, Type.EmptyTypes);
currGetIL = currGetPropMthdBldr.GetILGenerator();
currGetIL.Emit(OpCodes.Ldarg_0);
currGetIL.Emit(OpCodes.Ldfld, field);
currGetIL.Emit(OpCodes.Ret);
currSetPropMthdBldr = typeBuilder.DefineMethod("set_value", GetSetAttr, null, new Type[] { propertieInfo.PropertieType });
currSetIL = currSetPropMthdBldr.GetILGenerator();
currSetIL.Emit(OpCodes.Ldarg_0);
currSetIL.Emit(OpCodes.Ldarg_1);
currSetIL.Emit(OpCodes.Stfld, field);
currSetIL.Emit(OpCodes.Ret);
// Last, we must map the two methods created above to our PropertyBuilder to
// their corresponding behaviors, "get" and "set" respectively.
property.SetGetMethod(currGetPropMthdBldr);
property.SetSetMethod(currSetPropMthdBldr);
caType = typeof(Reportable);
objList.Clear();
objList.Add(propertieInfo.MemberToDataBind);
objList.Add(propertieInfo.ControlToUse);
objList.Add(propertieInfo.PropertieName);
if (propertieInfo.ControlToUse == ControlToUse.SystemItemTable)
{
objList.Add(propertieInfo.PropertieInnerCollectionType);
objList.Add(propertieInfo.PropertieInnerCollectionName);
objList.Add(propertieInfo.DisplayName);
objList.Add(-1);
objList.Add(FieldListIcon.UnUsedItem);
objList.Add(propertieInfo.SystemItemKey);
objList.Add(null);
}
else if (propertieInfo.ControlToUse == ControlToUse.AttributeTable)
{
objList.Add(null);
objList.Add(null);
objList.Add(null);
objList.Add(propertieInfo.MultiAttributeId);
objList.Add(FieldListIcon.UnUsedItem);
objList.Add(null);
objList.Add(null);
}
else if (propertieInfo.ControlToUse == ControlToUse.GUIGroupTable)
{
objList.Add(propertieInfo.PropertieInnerCollectionType);
objList.Add(propertieInfo.PropertieInnerCollectionName);
objList.Add(propertieInfo.DisplayName);
objList.Add(-1);
objList.Add(FieldListIcon.UnUsedItem);
objList.Add(null);
objList.Add(propertieInfo.GuiGroupKey);
}
else
{
objList.Add(null);
objList.Add(null);
objList.Add(null);
objList.Add(-1);
objList.Add(FieldListIcon.UnUsedItem);
objList.Add(null);
objList.Add(null);
}
var conInfo = caType.GetConstructor(Type.EmptyTypes);
var conArgs = new object[] { };
var caTypeFields = caType.GetFields();
caBuilder = new CustomAttributeBuilder(conInfo, conArgs, caTypeFields, objList.ToArray());
property.SetCustomAttribute(caBuilder);
caType = typeof(DisplayNameAttribute);
if (propertieInfo.IsList)
caBuilder = new CustomAttributeBuilder(caType.GetConstructor(new Type[] { typeof(string) }), new string[] { propertieInfo.DisplayName });
else
caBuilder = new CustomAttributeBuilder(caType.GetConstructor(new Type[] { typeof(string) }), new string[] { propertieInfo.DisplayName });
property.SetCustomAttribute(caBuilder);
}
return typeBuilder.CreateType();
}
要将动态程序集保存到自定义文件夹,您可以使用 AppDomain 的重载 DefineDynamicAssembly 方法
AppDomain currDom = AppDomain.CurrentDomain;
AssemblyName aName = new AssemblyName();
aName.Name = "YouAssemblyName";
string moduleName = aName.Name + ".dll";
AssemblyBuilder ab = currDom.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Save, @"FOLDER\TO\YOUR\ASSEMBLY");
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, moduleName);
TypeBuilder tb = mb.DefineType("Example");
tb.CreateType();
// type definition here
ab.Save(moduleName);
对于从自定义位置自动加载程序集,您可以使用 AppDomain 的 TypeResolve 事件
AppDomain.CurrentDomain.TypeResolve += CurrentDomain_TypeResolve;
Assembly CurrentDomain_TypeResolve(object sender, ResolveEventArgs args)
{
return Assembly.LoadFrom(/*path to your saved assembly*/);
}
适合此用途的文件夹是:驱动器盘符:\Users\\AppData\Local\YourFolder。
使用 GetFolderPath 方法获取其路径:
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
p.s。我稍微调整了这个文档中的示例:https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.typeresolve?view=netframework-4.8
更新:
最初的问题是关于如何 save/load 动态程序集 to/from 自定义位置。但是如果需要在一个AppDomain中定义内存中的动态模块和类型,又不需要保存动态汇编,那么调用AssemblyBuilder.DefineDynamicAssembly就可以了AssemblyBuilderAccess.Run 然后使用 AssemblyBuilder.DefineDynamicModule(string) 方法定义瞬态动态模块。
最后你应该调用 AssemblyBuilder.GetType(string, bool) 来获取你的类型。
代码会是这样
AppDomain currDom = AppDomain.CurrentDomain;
AssemblyName aName = new AssemblyName();
aName.Name = "YourAssemblyName";
AssemblyBuilder assemblyBuilder = currDom.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run); // We need Run access, don't need Save access
ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(aName.Name); // Here we define transient dynamic module in assembly
TypeBuilder tb = mb.DefineType("YourTypeName");
tb.CreateType();
// Define your type definition here
// ...
Type t = assemblyBuilder.GetType("YourTypeName", true); // Load created type using our AssemblyBuilder instance
Console.WriteLine("Loaded type \"{0}\".", t);
Object o = Activator.CreateInstance(t); // Create object of loaded type
Console.WriteLine("Created object \"{0}\".", o.ToString());
我正在 .NET 4.7.2 的运行时期间使用 TypeBuilder 创建 c# class。问题是该类型的 DLL 存储在当前应用程序的根文件夹中。问题是用户通常没有对本地根文件夹的写入权限。
那么如何设置程序集的保存和加载位置呢?什么用户文件夹适合这个?
当前代码:
private static Type CreateRaportType(List<PropertieInformation> propertieList, string className)
{
AssemblyName assemblyName;
AssemblyBuilder assemblyBuilder;
ModuleBuilder module;
TypeBuilder typeBuilder;
FieldBuilder field;
PropertyBuilder property;
MethodAttributes GetSetAttr;
MethodBuilder currGetPropMthdBldr;
MethodBuilder currSetPropMthdBldr;
ILGenerator currGetIL;
ILGenerator currSetIL;
Type caType;
CustomAttributeBuilder caBuilder;
List<Object> objList = new List<object>();
assemblyName = new AssemblyName();
assemblyName.Name = "ReportAssembly";
assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
module = assemblyBuilder.DefineDynamicModule("ReportModule");
typeBuilder = module.DefineType(className, TypeAttributes.Public | TypeAttributes.Class, typeof(GeneratedClassBase));
foreach (PropertieInformation propertieInfo in propertieList)
{
field = typeBuilder.DefineField("_" + propertieInfo.PropertieName, propertieInfo.PropertieType, FieldAttributes.Private);
property = typeBuilder.DefineProperty(propertieInfo.PropertieName, PropertyAttributes.None, propertieInfo.PropertieType, new Type[] { propertieInfo.PropertieType });
GetSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig;
currGetPropMthdBldr = typeBuilder.DefineMethod("get_value", GetSetAttr, propertieInfo.PropertieType, Type.EmptyTypes);
currGetIL = currGetPropMthdBldr.GetILGenerator();
currGetIL.Emit(OpCodes.Ldarg_0);
currGetIL.Emit(OpCodes.Ldfld, field);
currGetIL.Emit(OpCodes.Ret);
currSetPropMthdBldr = typeBuilder.DefineMethod("set_value", GetSetAttr, null, new Type[] { propertieInfo.PropertieType });
currSetIL = currSetPropMthdBldr.GetILGenerator();
currSetIL.Emit(OpCodes.Ldarg_0);
currSetIL.Emit(OpCodes.Ldarg_1);
currSetIL.Emit(OpCodes.Stfld, field);
currSetIL.Emit(OpCodes.Ret);
// Last, we must map the two methods created above to our PropertyBuilder to
// their corresponding behaviors, "get" and "set" respectively.
property.SetGetMethod(currGetPropMthdBldr);
property.SetSetMethod(currSetPropMthdBldr);
caType = typeof(Reportable);
objList.Clear();
objList.Add(propertieInfo.MemberToDataBind);
objList.Add(propertieInfo.ControlToUse);
objList.Add(propertieInfo.PropertieName);
if (propertieInfo.ControlToUse == ControlToUse.SystemItemTable)
{
objList.Add(propertieInfo.PropertieInnerCollectionType);
objList.Add(propertieInfo.PropertieInnerCollectionName);
objList.Add(propertieInfo.DisplayName);
objList.Add(-1);
objList.Add(FieldListIcon.UnUsedItem);
objList.Add(propertieInfo.SystemItemKey);
objList.Add(null);
}
else if (propertieInfo.ControlToUse == ControlToUse.AttributeTable)
{
objList.Add(null);
objList.Add(null);
objList.Add(null);
objList.Add(propertieInfo.MultiAttributeId);
objList.Add(FieldListIcon.UnUsedItem);
objList.Add(null);
objList.Add(null);
}
else if (propertieInfo.ControlToUse == ControlToUse.GUIGroupTable)
{
objList.Add(propertieInfo.PropertieInnerCollectionType);
objList.Add(propertieInfo.PropertieInnerCollectionName);
objList.Add(propertieInfo.DisplayName);
objList.Add(-1);
objList.Add(FieldListIcon.UnUsedItem);
objList.Add(null);
objList.Add(propertieInfo.GuiGroupKey);
}
else
{
objList.Add(null);
objList.Add(null);
objList.Add(null);
objList.Add(-1);
objList.Add(FieldListIcon.UnUsedItem);
objList.Add(null);
objList.Add(null);
}
var conInfo = caType.GetConstructor(Type.EmptyTypes);
var conArgs = new object[] { };
var caTypeFields = caType.GetFields();
caBuilder = new CustomAttributeBuilder(conInfo, conArgs, caTypeFields, objList.ToArray());
property.SetCustomAttribute(caBuilder);
caType = typeof(DisplayNameAttribute);
if (propertieInfo.IsList)
caBuilder = new CustomAttributeBuilder(caType.GetConstructor(new Type[] { typeof(string) }), new string[] { propertieInfo.DisplayName });
else
caBuilder = new CustomAttributeBuilder(caType.GetConstructor(new Type[] { typeof(string) }), new string[] { propertieInfo.DisplayName });
property.SetCustomAttribute(caBuilder);
}
return typeBuilder.CreateType();
}
要将动态程序集保存到自定义文件夹,您可以使用 AppDomain 的重载 DefineDynamicAssembly 方法
AppDomain currDom = AppDomain.CurrentDomain;
AssemblyName aName = new AssemblyName();
aName.Name = "YouAssemblyName";
string moduleName = aName.Name + ".dll";
AssemblyBuilder ab = currDom.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Save, @"FOLDER\TO\YOUR\ASSEMBLY");
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, moduleName);
TypeBuilder tb = mb.DefineType("Example");
tb.CreateType();
// type definition here
ab.Save(moduleName);
对于从自定义位置自动加载程序集,您可以使用 AppDomain 的 TypeResolve 事件
AppDomain.CurrentDomain.TypeResolve += CurrentDomain_TypeResolve;
Assembly CurrentDomain_TypeResolve(object sender, ResolveEventArgs args)
{
return Assembly.LoadFrom(/*path to your saved assembly*/);
}
适合此用途的文件夹是:驱动器盘符:\Users\\AppData\Local\YourFolder。 使用 GetFolderPath 方法获取其路径:
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
p.s。我稍微调整了这个文档中的示例:https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.typeresolve?view=netframework-4.8
更新:
最初的问题是关于如何 save/load 动态程序集 to/from 自定义位置。但是如果需要在一个AppDomain中定义内存中的动态模块和类型,又不需要保存动态汇编,那么调用AssemblyBuilder.DefineDynamicAssembly就可以了AssemblyBuilderAccess.Run 然后使用 AssemblyBuilder.DefineDynamicModule(string) 方法定义瞬态动态模块。 最后你应该调用 AssemblyBuilder.GetType(string, bool) 来获取你的类型。
代码会是这样
AppDomain currDom = AppDomain.CurrentDomain;
AssemblyName aName = new AssemblyName();
aName.Name = "YourAssemblyName";
AssemblyBuilder assemblyBuilder = currDom.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run); // We need Run access, don't need Save access
ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(aName.Name); // Here we define transient dynamic module in assembly
TypeBuilder tb = mb.DefineType("YourTypeName");
tb.CreateType();
// Define your type definition here
// ...
Type t = assemblyBuilder.GetType("YourTypeName", true); // Load created type using our AssemblyBuilder instance
Console.WriteLine("Loaded type \"{0}\".", t);
Object o = Activator.CreateInstance(t); // Create object of loaded type
Console.WriteLine("Created object \"{0}\".", o.ToString());