I have the following code which dynamically generates an assembly and classes based on an EdmModel. This is all working fine, however I a bit confused on how I can update the assembly when the model changes.
Here are my current thoughts, either:
Update the current classes, adding/removing properties. Adding new classes when needed.
Create another version of the assembly, allowing me to have the two versions running at the same time (This approach is preferable as it would allow the user to access the different versions of the model, and would mean the current version could still be used while the new one was being generated)
Is it possible to run two versions of the same assembly this way? Is it possible to update a type that has already been created? Am I doing this right?
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;
}
}
Update the current classes, adding/removing properties.
Not possible. Once you call the TypeBuilder.CreateType() the type you created can't be removed/modified.
Adding new classes when needed.
Possible without problems. You can use the types you define in your AssemblyBuilder without saving it, so it is possible to add new types.
Create another version of the assembly, allowing me to have the two versions running at the same time
No problem here.
Is it possible to run two versions of the same assembly this way?
It wouldn't be the same assembly for .NET... What do they have in common? Your abstract concept that "they are the same"? But the .NET can't look into your brain :-)
Related
I am trying to build dynamic types, inherited from a base object, based on a definition object (fieldnames/types) and then apply (potentially multiple) interfaces to that.
The base type has an object array to store values. The getters and setters use a generic method in the base type to save a specific type to an array location.
The "CreateType" works, but when I try to instantiate the object, I get:
System.InvalidProgramException : Common Language Runtime detected an invalid program.
I can't tell where the failure occurs.
Here is the Object definition:
public class UTObjectDef : IUTObjectDef
{
List<UTFieldDef> fieldDefs = new List<UTFieldDef>();
Dictionary<string, Type> interfaces = new Dictionary<string,Type>();
List<string> nameMap = new List<String>();
public string Name {get; set;}
public IUTFieldDef[] FieldDefs {
get {
return fieldDefs.ToArray();
}
}
public Type[] Interfaces {
get {
return interfaces.Values.ToArray();
}
}
public UTObjectDef(string name) {
Name = name;
}
public UTObjectDef(Type fromInterface) {
PropertyInfo[] props = fromInterface.GetProperties();
Name = fromInterface.Name;
if (Name.ToUpper().StartsWith("I")) {
Name = Name.Substring(1);
}
foreach( PropertyInfo prop in props) {
AddField(prop.Name, prop.PropertyType);
}
interfaces.Add(Name, fromInterface);
}
public void AddField(string name, Type fieldType)
{
var fieldDef = new UTFieldDef(name, fieldType);
fieldDefs.Add(fieldDef);
nameMap.Add(name);
}
public int FieldIndex(string fieldName) {
return nameMap.IndexOf(fieldName);
}
}
And the field definition:
public class UTFieldDef : IUTFieldDef
{
public string FieldName {get; set;}
public Type FieldType {get; set;}
public int? Length {get; set;}
public int? Decimals {get; set;}
public UTFieldDef(string fieldName, Type fieldType) {
FieldName = fieldName;
FieldType = fieldType;
}
}
The base type (Note: The constructor sets the arrays to the right size based on the field count), and the static UTObjectDef is populated through an Initialize static method, added in the TypeBuilder):
public class UTObject : IUTObject{
protected Object?[] values = new Object[0];
protected Object?[] origValues = new Object[0];
public T? getValue<T>(int index) {
return (T?)values[index];
}
public void setValue<T>(int index, T? value) {
values[index] = value;
}
}
And the dynamic object construction:
public static void buildTypes() {
MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);
Type baseType = typeof(UTObject.UTObject);
Type objDefType = typeof(UTObjectDef);
var valuesField = baseType.GetField("values", BindingFlags.NonPublic | BindingFlags.Instance);
var origValuesField = baseType.GetField("origValues", BindingFlags.NonPublic | BindingFlags.Instance);
foreach (UTObjectDef oDef in objectDefs.Values) {
TypeBuilder tb = mb.DefineType( oDef.Name, TypeAttributes.Public, baseType);
// Create a static field to hold the value.
const string FieldName = "ObjectDef";
var field = tb.DefineField(
FieldName,
objDefType,
FieldAttributes.Static | FieldAttributes.InitOnly | FieldAttributes.Public);
// Create a method to initialize the field.
const string InitializeMethodName = "Initialize";
var initMethod = tb.DefineMethod(
InitializeMethodName,
MethodAttributes.Static | MethodAttributes.Private,
CallingConventions.Standard,
typeof(void),
new[] { objDefType });
var il = initMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stsfld, field);
il.Emit(OpCodes.Ret);
System.Type[] param = {};
Type invalOp = typeof(InvalidOperationException);
ConstructorInfo invOpExInfo = invalOp.GetConstructor(
Type.EmptyTypes
);
ConstructorInfo baseCon = baseType.GetConstructor(Type.EmptyTypes);
ConstructorBuilder ctor = tb.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, param );
ILGenerator cil = ctor.GetILGenerator();
Label isInit = cil.DefineLabel(); // define label to jump in case condition is false
MethodInfo getFldDefsInfo = objDefType.GetMethod("get_FieldDefs");
cil.Emit(OpCodes.Ldarg_0);
cil.Emit(OpCodes.Call, baseCon );
cil.Emit(OpCodes.Ldsfld, field);
cil.Emit(OpCodes.Ldnull);
cil.Emit(OpCodes.Ceq);
cil.Emit(OpCodes.Brfalse, isInit);
cil.Emit(OpCodes.Nop);
cil.Emit(OpCodes.Ldstr, "Not Initialized");
cil.Emit(OpCodes.Newobj, invOpExInfo);
cil.ThrowException(invalOp);
cil.MarkLabel(isInit);
cil.Emit(OpCodes.Nop);
cil.Emit(OpCodes.Ldarg_0);
cil.Emit(OpCodes.Call, getFldDefsInfo);
cil.Emit(OpCodes.Ldlen);
cil.Emit(OpCodes.Conv_I4);
cil.Emit(OpCodes.Newarr, typeof(System.Object));
cil.Emit(OpCodes.Stfld, valuesField);
cil.Emit(OpCodes.Ldarg_0);
cil.Emit(OpCodes.Call, getFldDefsInfo);
cil.Emit(OpCodes.Ldlen);
cil.Emit(OpCodes.Conv_I4);
cil.Emit(OpCodes.Newarr, typeof(System.Object));
cil.Emit(OpCodes.Stfld, origValuesField);
cil.Emit(OpCodes.Ret);
MethodInfo[] methods = typeof(UTObject.UTObject).GetMethods();
MethodInfo? getMethod = null;
MethodInfo? setMethod = null;
foreach(MethodInfo mi in methods) {
if (mi.Name.Contains("getValue") && mi.IsGenericMethod) {
getMethod = mi;
}
if (mi.Name.Contains("setValue") && mi.IsGenericMethod) {
setMethod = mi;
}
}
for (int i = 0; i < oDef.FieldDefs.Length; i++ )
{
IUTFieldDef fDef = oDef.FieldDefs[i];
PropertyBuilder fldPropBldr = tb.DefineProperty(fDef.FieldName,
PropertyAttributes.None,
fDef.FieldType,
null);
MethodBuilder getMethodBuilder = tb.DefineMethod("get_" + fDef.FieldName,
getSetAttr,
fDef.FieldType,
Type.EmptyTypes);
if (getMethod != null) {
ILGenerator GetPropGetIL = getMethodBuilder.GetILGenerator();
LocalBuilder fldIdx = GetPropGetIL.DeclareLocal(typeof(Int32));
MethodInfo getMethodInfo = getMethod.MakeGenericMethod(fDef.FieldType);
GetPropGetIL.Emit(OpCodes.Ldc_I4, i);
GetPropGetIL.Emit(OpCodes.Stloc, fldIdx);
GetPropGetIL.Emit(OpCodes.Ldarg_0);
GetPropGetIL.Emit(OpCodes.Ldloc, fldIdx);
GetPropGetIL.Emit(OpCodes.Call, getMethodInfo);
GetPropGetIL.Emit(OpCodes.Ret);
}
MethodBuilder setMethodBuilder = tb.DefineMethod("set_" + fDef.FieldName,
getSetAttr,
null,
new Type[] { fDef.FieldType });
if (setMethod != null) {
ILGenerator SetPropGetIL = setMethodBuilder.GetILGenerator();
LocalBuilder fldIdx = SetPropGetIL.DeclareLocal(typeof(Int32));
SetPropGetIL.Emit(OpCodes.Ldc_I4, i);
SetPropGetIL.Emit(OpCodes.Stloc, fldIdx);
SetPropGetIL.Emit(OpCodes.Ldarg_0);
SetPropGetIL.Emit(OpCodes.Ldloc, fldIdx);
SetPropGetIL.Emit(OpCodes.Call, setMethod.MakeGenericMethod(fDef.FieldType));
SetPropGetIL.Emit(OpCodes.Nop);
SetPropGetIL.Emit(OpCodes.Ret);
}
fldPropBldr.SetGetMethod(getMethodBuilder);
fldPropBldr.SetSetMethod(setMethodBuilder);
}
/* foreach(Type interfaceType in oDef.Interfaces) {
tb.AddInterfaceImplementation(interfaceType);
}
*/
Type? newType = tb.CreateType();
// Invoke the initializer method using reflection, passing the provided value to initialize the new field.
MethodInfo initMethodInfo = newType.GetMethod(InitializeMethodName, BindingFlags.Static | BindingFlags.NonPublic);
if (initMethodInfo != null) {
initMethodInfo.Invoke(null, new object[] { oDef });
}
foreach(Type interfaceType in oDef.Interfaces) {
objectTypesByInterface.Add(interfaceType.Name, newType);
}
}
I'm using an object factory for dynamically creating instances by interface with help of reflection.
My code:
public static class ObjectFactory
{
private static readonly ConcurrentDictionary<Type, Type> TypeCache = new ConcurrentDictionary<Type, Type>();
public static T CreateInstance<T>(Type parent = null)
{
if (!typeof(T).IsInterface) throw new ArgumentException($"Type {typeof(T).Name} must be an interface.");
var newType = TypeCache.GetOrAdd(typeof(T), t => BuildType(typeof(T), parent));
return (T)Activator.CreateInstance(newType);
}
private static Type BuildType(Type interfaceType, Type parent = null)
{
var assemblyName = new AssemblyName($"DynamicAssembly_{Guid.NewGuid():N}");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
var typeName = $"{RemoveInterfacePrefix(interfaceType.Name)}_{Guid.NewGuid():N}";
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
if (parent != null)
typeBuilder.SetParent(parent);
typeBuilder.AddInterfaceImplementation(interfaceType);
var properties = interfaceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var methods = interfaceType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
BuildProperties(typeBuilder, properties);
BuildMethods(typeBuilder, methods);
return typeBuilder.CreateType();
string RemoveInterfacePrefix(string name) => Regex.Replace(name, "^I", string.Empty);
}
private static void BuildMethods(TypeBuilder typeBuilder, IEnumerable<MethodInfo> methods)
{
foreach (var method in methods)
{
var paramTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
AddMethodDynamically(typeBuilder, method.Name, paramTypes, method.ReturnType, "A");
}
}
private static void BuildProperties(TypeBuilder typeBuilder, IEnumerable<PropertyInfo> properties)
{
foreach (var property in properties)
{
BuildProperty(typeBuilder, property);
}
}
private static PropertyBuilder BuildProperty(TypeBuilder typeBuilder, PropertyInfo property)
{
var fieldName = $"<{property.Name}>k__BackingField";
var propertyBuilder = typeBuilder.DefineProperty(property.Name, System.Reflection.PropertyAttributes.None, property.PropertyType, Type.EmptyTypes);
// Build backing-field.
var fieldBuilder = typeBuilder.DefineField(fieldName, property.PropertyType, FieldAttributes.Private);
var getSetAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;
var getterBuilder = BuildGetter(typeBuilder, property, fieldBuilder, getSetAttributes);
var setterBuilder = BuildSetter(typeBuilder, property, fieldBuilder, getSetAttributes);
propertyBuilder.SetGetMethod(getterBuilder);
propertyBuilder.SetSetMethod(setterBuilder);
return propertyBuilder;
}
private static MethodBuilder BuildGetter(TypeBuilder typeBuilder, PropertyInfo property, FieldBuilder fieldBuilder, MethodAttributes attributes)
{
var getterBuilder = typeBuilder.DefineMethod($"get_{property.Name}", attributes, property.PropertyType, Type.EmptyTypes);
var ilGenerator = getterBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
if (property.GetCustomAttribute<NotNullAttribute>() != null)
{
// Build null check
ilGenerator.Emit(OpCodes.Dup);
var isFieldNull = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Brtrue_S, isFieldNull);
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Ldstr, $"{property.Name} isn't set.");
var invalidOperationExceptionConstructor = typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) });
ilGenerator.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor);
ilGenerator.Emit(OpCodes.Throw);
ilGenerator.MarkLabel(isFieldNull);
}
ilGenerator.Emit(OpCodes.Ret);
return getterBuilder;
}
private static void AddMethodDynamically(TypeBuilder myTypeBld,
string mthdName,
Type[] mthdParams,
Type returnType,
string mthdAction)
{
MethodBuilder myMthdBld = myTypeBld.DefineMethod(
mthdName,
MethodAttributes.Public,
returnType,
mthdParams);
ILGenerator ILout = myMthdBld.GetILGenerator();
int numParams = mthdParams.Length;
for (byte x = 0; x < numParams; x++)
{
ILout.Emit(OpCodes.Ldarg_S, x);
}
if (numParams > 1)
{
for (int y = 0; y < (numParams - 1); y++)
{
switch (mthdAction)
{
case "A":
ILout.Emit(OpCodes.Add);
break;
case "M":
ILout.Emit(OpCodes.Mul);
break;
default:
ILout.Emit(OpCodes.Add);
break;
}
}
}
ILout.Emit(OpCodes.Ret);
}
private static MethodBuilder BuildSetter(TypeBuilder typeBuilder, PropertyInfo property, FieldBuilder fieldBuilder, MethodAttributes attributes)
{
var setterBuilder = typeBuilder.DefineMethod($"set_{property.Name}", attributes, null, new Type[] { property.PropertyType });
var ilGenerator = setterBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
// Build null check
if (property.GetCustomAttribute<NotNullAttribute>() != null)
{
var isValueNull = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Dup);
ilGenerator.Emit(OpCodes.Brtrue_S, isValueNull);
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Ldstr, property.Name);
var argumentNullExceptionConstructor = typeof(ArgumentNullException).GetConstructor(new Type[] { typeof(string) });
ilGenerator.Emit(OpCodes.Newobj, argumentNullExceptionConstructor);
ilGenerator.Emit(OpCodes.Throw);
ilGenerator.MarkLabel(isValueNull);
}
ilGenerator.Emit(OpCodes.Stfld, fieldBuilder);
ilGenerator.Emit(OpCodes.Ret);
return setterBuilder;
}
}
Method BuildMethods isn't creating implementation method by interface.
Please help solve 2 problems:
Create implementation method by interface;
All created classes have parent classes and all methods on interface after implementation have only executed method by parent class/ for example
public class Parent
{
public T Get<T, Y>(string url, params object[] query) => httpClient.Get<T>(url, query);
public T Post<T, Y>(string url, params Y model) => httpClient.Post<T>(url, model);
}
Create interface:
public inteface IRest
{
[HttpGet("/api/user-ids")] List<string> GetUserIds(int count, int skip);
}
What I want in finally on create instance by interface, I got instance where method GetUserIds execute Get<T, Y> from parent class
var rest = ObjectFactory.CreateInstance<IRest>(typeof(Parent));
var userIds = rest.GetUserIds(10, 0);
I want to implement an interface at runtime, however when i execute
return Activator.CreateInstance(typeBuilder.CreateTypeInfo().AsType(), new RPCRequestProxy());
in RPCClientFactory.cs it throws
System.TypeLoadException: Signature of the body and declaration in a
method implementation do not match.
I use dotnet core 1.0.4.
The entry point Program.cs :
namespace RPC
{
interface IRCPClient
{
Task Foo([UrlParam]int b, [UrlParam]string a, [UrlParam] DateTime c);
}
class Program
{
static void Main(string[] args)
{
var factory = new RPCClientFactory();
factory.Register<IRCPClient>();
var cli = factory.GetClient<IRCPClient>();
cli.Foo(1, "HelloWorld", DateTime.Now);
Console.ReadKey();
}
}
}
The RPCClientFactory.cs:
public class RPCClientFactory
{
private readonly ConcurrentDictionary<Type, object> _clients = new ConcurrentDictionary<Type, object>();
public void Register<ClientType>()
{
Register(typeof(ClientType));
}
public void Register(Type type)
{
if (!_clients.TryAdd(type, CreateImplInstance(type)))
throw new InvalidOperationException($"bad type{type}");
}
public object CreateImplInstance(Type type)
{
var typeBuilder = CreateTypeBuilder(type);
var methods = type.GetMethods();
var field = CreateFiled(typeBuilder);
CreateCotr(typeBuilder, field);
CreateMethods(typeBuilder, methods, field);
typeBuilder.AddInterfaceImplementation(type);
return Activator.CreateInstance(typeBuilder.CreateTypeInfo().AsType(), new RPCRequestProxy());
}
private static TypeBuilder CreateTypeBuilder(Type type)
{
var typeSignature = "MyDynamicType";
var an = new AssemblyName(typeSignature);
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);
return tb;
}
private static FieldInfo CreateFiled(TypeBuilder typeBuilder)
{
return typeBuilder.DefineField("_proxy", typeof(RPCRequestProxy), FieldAttributes.Private);
}
private static void CreateCotr(TypeBuilder typeBuilder, FieldInfo proxyField)
{
var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public
, CallingConventions.Standard
, new[] { typeof(RPCRequestProxy) });
var il = ctorBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, proxyField);
il.Emit(OpCodes.Ret);
}
public void CreateMethods(TypeBuilder typeBuilder, MethodInfo[] methods, FieldInfo field)
{
foreach (var method in methods)
{
CreateMethod(typeBuilder, method, field);
}
}
private static void CreateMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo proxyFiled)
{
var paramters = method.GetParameters();
var methodBuilder = typeBuilder.DefineMethod(method.Name,
MethodAttributes.Public,
CallingConventions.Standard,
method.ReturnType,
paramters.Select(x => x.GetType()).ToArray());
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Newobj, typeof(Dictionary<string, object>).GetConstructor(new Type[0]));
il.Emit(OpCodes.Stloc_0);
for (var i = 0; i < paramters.Length; ++i)
{
var param = paramters[i];
if (param.GetCustomAttribute<BodyParamAttribute>() != null) continue;
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, param.Name);
il.Emit(OpCodes.Ldarg, i + 1);
if (param.ParameterType.GetTypeInfo().IsValueType)
{
il.Emit(OpCodes.Box, param.GetType());
}
il.Emit(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("Add"));
}
var proxyMethod = typeof(RPCRequestProxy)
.GetMethods()
.First(x => x.Name == "PostAsync" && x.GetParameters().Length == 2);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, proxyFiled);
il.Emit(OpCodes.Ldstr, "xxx.xxx.xxx");
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Callvirt, proxyMethod);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(methodBuilder, method);
}
public ClientType GetClient<ClientType>() where ClientType : class
{
_clients.TryGetValue(typeof(ClientType), out object cli);
return cli as ClientType;
}
public ClientType GetRequiredClient<ClientType>() where ClientType : class
{
var cli = GetClient<ClientType>();
if (cli == null) throw new InvalidOperationException($"bad type{typeof(ClientType)}");
return cli;
}
}
The RPCRequestProxy.cs:
public class RPCRequestProxy
{
public RPCRequestProxy()
{
}
public async Task PostAsync(string url, IDictionary<string, object> urlParams)
{
await Task.Run(() =>
{
Console.WriteLine("URL:" + url);
Console.WriteLine("QueryString:");
foreach (var kv in urlParams)
{
Console.WriteLine($"{kv.Key}={kv.Value}");
}
});
}
}
After register IRCPClient, i hope it generates a class like this:
class RCPClient : IRCPClient
{
private readonly RPCRequestProxy _proxy;
public RCPClient(RPCRequestProxy proxy)
{
_proxy = proxy;
}
public Task Foo([UrlParam] int b, [UrlParam] string a, [UrlParam] DateTime c)
{
var urlParams = new Dictionary<string, object>();
urlParams.Add(nameof(b), b);
urlParams.Add(nameof(a), a);
urlParams.Add(nameof(c), c);
return _proxy.PostAsync("xxx.xxx.xxx", urlParams);
}
}
Finally, i worked it out:
1: We must DeclareLocal before we use a local value.
2: Change paramters.Select(x => x.GetType()).ToArray() to paramters.Select(x => x.ParameterType).ToArray()
This is my final CreateMethod method of RPCClientFactory.cs
private static void CreateMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo proxyFiled)
{
var paramters = method.GetParameters();
var methodBuilder = typeBuilder.DefineMethod(method.Name,
MethodAttributes.Public
| MethodAttributes.Virtual
| MethodAttributes.HideBySig
| MethodAttributes.SpecialName,
CallingConventions.Standard,
method.ReturnType,
paramters.Select(x => x.ParameterType).ToArray()); // change this line
var il = methodBuilder.GetILGenerator();
il.DeclareLocal(typeof(Dictionary<string, object>)); // add this line
il.Emit(OpCodes.Newobj, typeof(Dictionary<string, object>).GetConstructor(new Type[0]));
il.Emit(OpCodes.Stloc_0);
for (short i = 0; i < paramters.Length; ++i)
{
var param = paramters[i];
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, param.Name);
il.Emit(OpCodes.Ldarg, (short)(i +1));
if (param.ParameterType.GetTypeInfo().IsValueType)
{
il.Emit(OpCodes.Box, param.ParameterType);
}
il.Emit(OpCodes.Callvirt, typeof(Dictionary<string, object>).GetMethod("Add"));
}
var proxyMethod = typeof(RPCRequestProxy)
.GetMethods()
.First(x => x.Name == "PostAsync" && x.GetParameters().Length == 2);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, proxyFiled);
il.Emit(OpCodes.Ldstr, "xxx.xxx.xxx");
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Callvirt, proxyMethod);
il.Emit(OpCodes.Ret);
}
I want to know how to generator the following method myMethod with ilGenerator.
public class MyClass {
private MyField myField;
public int myMethod(int b) {
return myField.someMethod(b);
}
}
Can someone help me ?
class Program
{
static MethodAttributes attrs = MethodAttributes.Public | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
static Type CreateType() {
var myDomain = Thread.GetDomain();
var myAsmName = new AssemblyName { Name = "Demo" };
var myAsmBuilder = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.RunAndSave);
var moudle = myAsmBuilder.DefineDynamicModule("DemoModule", "Demo.dll");
var typeBuilder = moudle.DefineType("MyClass", TypeAttributes.Public | TypeAttributes.BeforeFieldInit);
var fieldBuilder = typeBuilder.DefineField("myField", typeof(MyField), FieldAttributes.Private);
var ctorBuilder = typeBuilder.DefineConstructor(attrs, CallingConventions.HasThis | CallingConventions.Standard, Type.EmptyTypes);
var methodBuilder = typeBuilder.DefineMethod("MyMethod", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(int), new[] { typeof(int) });
var ctorILGen = ctorBuilder.GetILGenerator();
var ilGen = methodBuilder.GetILGenerator();
var someMethod = fieldBuilder.FieldType.GetMethod("SomeMethod");
ctorILGen.Emit(OpCodes.Ldarg_0);
ctorILGen.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
ctorILGen.Emit(OpCodes.Ldarg_0);
ctorILGen.Emit(OpCodes.Newobj, fieldBuilder.FieldType.GetConstructor(Type.EmptyTypes));
ctorILGen.Emit(OpCodes.Stfld, fieldBuilder);
ctorILGen.Emit(OpCodes.Ret);
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldfld, fieldBuilder);
ilGen.Emit(OpCodes.Ldarg_1);
ilGen.Emit(OpCodes.Callvirt, someMethod);
ilGen.Emit(OpCodes.Ret);
return typeBuilder.CreateType();
}
static void Main(string[] args) {
var type = CreateType();
dynamic instance = Activator.CreateInstance(type);
Console.WriteLine(instance.MyMethod(10));
}
}
will generate
public class MyClass
{
private MyField myField;
public MyClass() {
myField = new MyField();
}
public int myMethod(int b) {
return myField.SomeMethod(b);
}
}
public class MyField
{
public int SomeMethod(int i) {
return i;
}
}
i have to use entity framework with dynamic table columns(that user can add column to my table), and i need to have dynamic run-time type entity to map my table.
my scenario :
if DbContext wants to insert static entity(like MyTableEntity) that inherited from DynamicEntity, it will create Dynamic type with same name of entity(like MyTableEntity) with "Dynamic_" prefix, after that create instance from generated runtime type and then fill static type properties value to
instance of runtime object. finally insert method will insert runtime object.
every thing is good and work.
but because TypeBuilder create runtime type and if i call insert two time or more, TypeBuilder will create new type with same name like "Dynamic_MyTableEntity" and DbContext cannot recognize witch class must insert and it is natural.
my question :
how can i remove old type that i create by TypeBuilder or renew or update old type, like first remove all properties and again add all properties.
i create class that inherited from DynamicObject, and i will inherit my dynamic entities from DynamicEntity class.
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;
}
}
sample my entity :
public class MyTableEntity : DynamicEntity {
[Key]
public int ID { get; set; }
}
sample use :
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();
finally i change my dbcontext and my problem solved
public class HREntities : DbContext {
public HREntities(string connectionString)
: base(connectionString) {
}
public HREntities(string connectionString, Type entityType)
: base(connectionString, GenerateDbCompiledModel(connectionString, entityType)) {
}
private static DbCompiledModel GenerateDbCompiledModel(string connectionString, Type entityType) {
string tableName;
if (typeof(DynamicEntity).IsAssignableFrom(entityType)) {
tableName = entityType.Name.Substring((DynamicEntity.DynamicTypePrefix + "tbl").Length);
}
else {
tableName = entityType.Name.Substring("tbl".Length);
}
DbModelBuilder dbModelBuilder = new DbModelBuilder(DbModelBuilderVersion.Latest);
var entityMethod = dbModelBuilder.GetType().GetMethod("Entity").MakeGenericMethod(entityType);
var entityTypeConfiguration = entityMethod.Invoke(dbModelBuilder, new object[0]);
entityTypeConfiguration.GetType().GetMethod("ToTable", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string) }, null).Invoke(entityTypeConfiguration, new object[] { tableName });
return dbModelBuilder.Build(new SqlConnection(connectionString)).Compile();
}
}
every time when i want create instance of HREntities i pass my dynamic type and my dbcontext work fine...