Im creating c# class during runtime in .NET 4.7.2 by using the TypeBuilder. The problem is that the DLL for the type is stored in the root folder of current application. The problem is that often the user have no write access to the local root folder.
So how do I set the location where the assemblies should be saved and loaded? And what user folder would be fitting for this?
Current code :
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();
}
For saving dynamic assembly to custom folder your can use overloaded DefineDynamicAssembly method of AppDomain
For automatic assembly loading from custom place your can use TypeResolve event of AppDomain
Good folder for this purpose is: driveletter:\Users\\AppData\Local\YourFolder. Use GetFolderPath method to get its path:
p.s. I slightly tweaked example from this doc: https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.typeresolve?view=netframework-4.8
UPDATED:
Initial question was about how to save/load dynamic assembly to/from custom location. But if it is necessary to define dynamic module and types in memory in one AppDomain and you don't need to save dynamic assembly, then it is enough to call AssemblyBuilder.DefineDynamicAssembly with AssemblyBuilderAccess.Run and then define transient dynamic module using AssemblyBuilder.DefineDynamicModule(string) method. Finally you should call AssemblyBuilder.GetType(string, bool) to get your type.
The code will be like this