I did write a model generator to create an interface between client ViewModels and server Models.
Some model classes are serialize with [JsonObject(MemberSerialization.OptIn)] attribute and their properties are marked with [JsonProperty ...] to be serialized, in addition i have some custom attributes which need to be handled in my custom DefaultContractResolver.
Here is how I create my ModelView from any source type
public Type CreateModelView(Type sourceType) {
...
foreach (var attribute in sourceType.CustomAttributes)
typeBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
attribute.Constructor,
attribute.ConstructorArguments.Select(x => x.Value).ToArray()));
...
var fields = GetFields(sourceType);
foreach (var field in fields)
CreateProperty(typeBuilder, field.Name, field.PropertyType, field.GetCustomAttributes(true));
return typeBuilder.CreateTypeInfo().AsType();
}
private void CreateProperty(TypeBuilder typeBuilder, string name, Type type, object[] customAttributes)
{
var fieldBuilder = typeBuilder.DefineField(name, type, FieldAttributes.Public);
var propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.HasDefault, type, null);
var GetMethodBuilder = typeBuilder.DefineMethod(
"Get" + name,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig, type,
Type.EmptyTypes);
var iLGenerator = GetMethodBuilder.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
iLGenerator.Emit(OpCodes.Ret);
var SetMethodBuilder =
typeBuilder.DefineMethod("Set" + name,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null,
new[] { type });
ILGenerator setIl = SetMethodBuilder.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
foreach (var customAttribute in customAttributes)
AddCustomAttributeToProperty(customAttribute, propertyBuilder);
propertyBuilder.SetGetMethod(GetMethodBuilder);
propertyBuilder.SetSetMethod(SetMethodBuilder);
}
/// <summary>
/// Given a custom attribute and property builder, adds an instance of custom attribute
/// to the property builder
/// </summary>
void AddCustomAttributeToProperty(object customAttribute, PropertyBuilder propBuilder)
{
var customAttributeBuilder = BuildCustomAttribute(customAttribute);
if (customAttributeBuilder != null)
{
propBuilder.SetCustomAttribute(customAttributeBuilder);
}
}
static CustomAttributeBuilder BuildCustomAttribute(object customAttribute)
{
ConstructorInfo longestCtor = null;
// Get constructor with the largest number of parameters
foreach (var cInfo in customAttribute.GetType().GetConstructors().
Where(cInfo => longestCtor == null || longestCtor.GetParameters().Length < cInfo.GetParameters().Length))
longestCtor = cInfo;
if (longestCtor == null)
{
return null;
}
// For each constructor parameter, get corresponding (by name similarity) property and get its value
var args = new object[longestCtor.GetParameters().Length];
var position = 0;
foreach (var consParamInfo in longestCtor.GetParameters())
{
var attrPropInfo = customAttribute.GetType().GetProperty(consParamInfo.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (attrPropInfo != null)
{
args[position] = attrPropInfo.GetValue(customAttribute, null);
}
else
{
args[position] = null;
var attrFieldInfo = customAttribute.GetType().GetField(consParamInfo.Name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (attrFieldInfo == null)
{
if (consParamInfo.ParameterType.IsValueType)
{
args[position] = Activator.CreateInstance(consParamInfo.ParameterType);
}
}
else
{
args[position] = attrFieldInfo.GetValue(customAttribute);
}
}
++position;
}
var propList = new List<PropertyInfo>();
var propValueList = new List<object>();
foreach (var attrPropInfo in customAttribute.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!attrPropInfo.CanWrite)
{
continue;
}
object defaultValue = null;
var defaultAttributes = attrPropInfo.GetCustomAttributes(typeof(DefaultValueAttribute), true);
if (defaultAttributes.Length > 0)
{
defaultValue = ((DefaultValueAttribute)defaultAttributes[0]).Value;
}
var value = attrPropInfo.GetValue(customAttribute, null);
if (value == defaultValue)
{
continue;
}
propList.Add(attrPropInfo);
propValueList.Add(value);
}
return new CustomAttributeBuilder(longestCtor, args, propList.ToArray(), propValueList.ToArray());
}
i tested this code as well with field.CustomAttributes and result was same.
propertyBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
attribute.Constructor,
attribute.ConstructorArguments.Select(x => x.Value).ToArray()));
and the problem is here in ContractResolver when the result of attributes is empty,
public class ContractResolver : DefaultContractResolver {
...
protected override IList<JsonProperty> CreateProperties(
Type type,
MemberSerialization memberSerialization)
{
var propertyList = base.CreateProperties(type, memberSerialization);
foreach (var jProperty in propertyList)
{
var attributes = jProperty.AttributeProvider.GetAttributes(true);
var isBindNever = attributes.OfType<BindNeverAttribute>().Any();
var isKey = attributes.OfType<KeyAttribute>().Any();
...
}
and [JsonProperty ...] attributes are not working as well.
P.S. I can get custom attributes from this code
var PropertyType = jProperty.DeclaringType.GetProperties()
.Where(property => property.Name.ToLower() == jProperty.PropertyName.ToLower())
.FirstOrDefault();
The problem is why is jProperty.AttributeProvider.GetAttributes not working as expected,
Why [JsonProperty] not working as expected (marked properties won't serialized).
This will solve problem, however it's not efficient
public class JsonResolver : DefaultContractResolver
{
...
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var serializableMembers = base.GetSerializableMembers(objectType).Select(memberInfo => memberInfo.Name);
return objectType.GetProperties().Where(memberInfo => serializableMembers.Contains(memberInfo.Name)).Cast<MemberInfo>().ToList();
}
...
}
Related
I'm trying to make a simple HtmlHelper to create a EditorFor that deal with List<T> index childs.
I did some like this
public static IHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper
, Expression<Func<TModel, TValue>> exp
, Expression<Func<TModel, int>> expId
, object htmlAttributes = null)
{
//Id value
var id = expId.Compile().Invoke(htmlHelper.ViewData.Model);
//Prefix
var prefix = htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix;
//Propertyname
var propertyName = ExpressionHelper.GetExpressionText(exp);
//Create id and name for the EditorFor
var htmlId = $"{prefix}_{id}__{propertyName}";
var htmlName = $"{prefix}[{id}].{propertyName}";
//Create the must have attributes.
var attributes = new
RouteValueDictionary(
new {
id = htmlId,
#Name = htmlName
});
//if htmlAttributes paramter has values, will merge
if(htmlAttributes != null){
//merge..
foreach (var attr in new RouteValueDictionary(htmlAttributes).Where(attr => !attributes.ContainsKey(attr.Key)))
{
attributes.Add(attr.Key,attr.Value);
}
}
//Create a editor for
var editor = htmlHelper.EditorFor(exp, new
{
htmlAttributes = attributes
});
return new HtmlString(editor.ToString());
}
In the view i'm using it like
#Html.EditorFor(x => x.GradienteDeEntrada, x => x.Id)
My problem here, is that the Html markup generated is
<input count="2" keys="System.Collections.Generic.Dictionary`2+KeyCollection[System.String,System.Object]" values="System.Collections.Generic.Dictionary`2+ValueCollection[System.String,System.Object]" autocomplete="off" class=" decimal" data-val="true" data-val-number="El campo Gradiente de entrada debe ser un nĂºmero." id="DatosTermicos_GradienteEntrada" name="DatosTermicos.GradienteEntrada" type="text" value="">
I try converting it to HtmlHelper with this answer or dynamic with this other but when i use any of this methods, both Id and Name are just ignored.
If I create the htmlAttributes as a object
var attributes = new { id = htmlId, #Name = htmlName };
work just fine, the problem is that I don't know how to merge it with the received parameters. How can I pass the RouteValueDictionary with my HtmlAttributes to the EditorFor?
I solve it using this answer from Abdul Rauf based on converting the RouteValueDictionary in Anonymous Object (System.Object) instead of dynamic.
This is the method
public static object FromDictToAnonymousObj<TValue>(IDictionary<string, TValue> dict)
{
var types = new Type[dict.Count];
for (int i = 0; i < types.Length; i++)
{
types[i] = typeof(TValue);
}
// dictionaries don't have an order, so we force an order based
// on the Key
var ordered = dict.OrderBy(x => x.Key).ToArray();
string[] names = Array.ConvertAll(ordered, x => x.Key);
Type type = AnonymousType.CreateType(types, names);
object[] values = Array.ConvertAll(ordered, x => (object)x.Value);
object obj = type.GetConstructor(types).Invoke(values);
return obj;
}
The AnonymousType class code :
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
/// <summary>
/// The code generated should be nearly equal to the one generated by
/// csc 12.0.31101.0 when compiling with /optimize+ /debug-. The main
/// difference is in the GetHashCode() (the base init_hash used is
/// compiler-dependant) and in the maxstack of the generated methods.
/// Note that Roslyn (at least the one present at
/// tryroslyn.azurewebsites.net) generates different code for anonymous
/// types.
/// </summary>
public static class AnonymousType
{
private static readonly ConcurrentDictionary<string, Type> GeneratedTypes = new ConcurrentDictionary<string, Type>();
private static readonly AssemblyBuilder AssemblyBuilder;
private static readonly ModuleBuilder ModuleBuilder;
private static readonly string FileName;
// Some objects we cache
private static readonly CustomAttributeBuilder CompilerGeneratedAttributeBuilder = new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
private static readonly CustomAttributeBuilder DebuggerBrowsableAttributeBuilder = new CustomAttributeBuilder(typeof(DebuggerBrowsableAttribute).GetConstructor(new[] { typeof(DebuggerBrowsableState) }), new object[] { DebuggerBrowsableState.Never });
private static readonly CustomAttributeBuilder DebuggerHiddenAttributeBuilder = new CustomAttributeBuilder(typeof(DebuggerHiddenAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
private static readonly ConstructorInfo ObjectCtor = typeof(object).GetConstructor(Type.EmptyTypes);
private static readonly MethodInfo ObjectToString = typeof(object).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null);
private static readonly ConstructorInfo StringBuilderCtor = typeof(StringBuilder).GetConstructor(Type.EmptyTypes);
private static readonly MethodInfo StringBuilderAppendString = typeof(StringBuilder).GetMethod("Append", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string) }, null);
private static readonly MethodInfo StringBuilderAppendObject = typeof(StringBuilder).GetMethod("Append", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(object) }, null);
private static readonly Type EqualityComparer = typeof(EqualityComparer<>);
private static readonly Type EqualityComparerGenericArgument = EqualityComparer.GetGenericArguments()[0];
private static readonly MethodInfo EqualityComparerDefault = EqualityComparer.GetMethod("get_Default", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null);
private static readonly MethodInfo EqualityComparerEquals = EqualityComparer.GetMethod("Equals", BindingFlags.Instance | BindingFlags.Public, null, new[] { EqualityComparerGenericArgument, EqualityComparerGenericArgument }, null);
private static readonly MethodInfo EqualityComparerGetHashCode = EqualityComparer.GetMethod("GetHashCode", BindingFlags.Instance | BindingFlags.Public, null, new[] { EqualityComparerGenericArgument }, null);
private static int Index = -1;
static AnonymousType()
{
var assemblyName = new AssemblyName("AnonymousTypes");
FileName = assemblyName.Name + ".dll";
AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder = AssemblyBuilder.DefineDynamicModule("AnonymousTypes", FileName);
}
public static void Dump()
{
AssemblyBuilder.Save(FileName);
}
/// <summary>
///
/// </summary>
/// <param name="types"></param>
/// <param name="names"></param>
/// <returns></returns>
public static Type CreateType(Type[] types, string[] names)
{
if (types == null)
{
throw new ArgumentNullException("types");
}
if (names == null)
{
throw new ArgumentNullException("names");
}
if (types.Length != names.Length)
{
throw new ArgumentException("names");
}
// Anonymous classes are generics based. The generic classes
// are distinguished by number of parameters and name of
// parameters. The specific types of the parameters are the
// generic arguments. We recreate this by creating a fullName
// composed of all the property names, separated by a "|"
string fullName = string.Join("|", names.Select(x => Escape(x)));
Type type;
if (!GeneratedTypes.TryGetValue(fullName, out type))
{
// We create only a single class at a time, through this lock
// Note that this is a variant of the double-checked locking.
// It is safe because we are using a thread safe class.
lock (GeneratedTypes)
{
if (!GeneratedTypes.TryGetValue(fullName, out type))
{
int index = Interlocked.Increment(ref Index);
string name = names.Length != 0 ? string.Format("<>f__AnonymousType{0}`{1}", index, names.Length) : string.Format("<>f__AnonymousType{0}", index);
TypeBuilder tb = ModuleBuilder.DefineType(name, TypeAttributes.AnsiClass | TypeAttributes.Class | TypeAttributes.AutoLayout | TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit);
tb.SetCustomAttribute(CompilerGeneratedAttributeBuilder);
GenericTypeParameterBuilder[] generics = null;
if (names.Length != 0)
{
string[] genericNames = Array.ConvertAll(names, x => string.Format("<{0}>j__TPar", x));
generics = tb.DefineGenericParameters(genericNames);
}
else
{
generics = new GenericTypeParameterBuilder[0];
}
// .ctor
ConstructorBuilder constructor = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, generics);
constructor.SetCustomAttribute(DebuggerHiddenAttributeBuilder);
ILGenerator ilgeneratorConstructor = constructor.GetILGenerator();
ilgeneratorConstructor.Emit(OpCodes.Ldarg_0);
ilgeneratorConstructor.Emit(OpCodes.Call, ObjectCtor);
var fields = new FieldBuilder[names.Length];
// There are two for cycles because we want to have
// all the getter methods before all the other
// methods
for (int i = 0; i < names.Length; i++)
{
// field
fields[i] = tb.DefineField(string.Format("<{0}>i__Field", names[i]), generics[i], FieldAttributes.Private | FieldAttributes.InitOnly);
fields[i].SetCustomAttribute(DebuggerBrowsableAttributeBuilder);
// .ctor
constructor.DefineParameter(i + 1, ParameterAttributes.None, names[i]);
ilgeneratorConstructor.Emit(OpCodes.Ldarg_0);
if (i == 0)
{
ilgeneratorConstructor.Emit(OpCodes.Ldarg_1);
}
else if (i == 1)
{
ilgeneratorConstructor.Emit(OpCodes.Ldarg_2);
}
else if (i == 2)
{
ilgeneratorConstructor.Emit(OpCodes.Ldarg_3);
}
else if (i < 255)
{
ilgeneratorConstructor.Emit(OpCodes.Ldarg_S, (byte)(i + 1));
}
else
{
// Ldarg uses a ushort, but the Emit only
// accepts short, so we use a unchecked(...),
// cast to short and let the CLR interpret it
// as ushort
ilgeneratorConstructor.Emit(OpCodes.Ldarg, unchecked((short)(i + 1)));
}
ilgeneratorConstructor.Emit(OpCodes.Stfld, fields[i]);
// getter
MethodBuilder getter = tb.DefineMethod(string.Format("get_{0}", names[i]), MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, CallingConventions.HasThis, generics[i], Type.EmptyTypes);
ILGenerator ilgeneratorGetter = getter.GetILGenerator();
ilgeneratorGetter.Emit(OpCodes.Ldarg_0);
ilgeneratorGetter.Emit(OpCodes.Ldfld, fields[i]);
ilgeneratorGetter.Emit(OpCodes.Ret);
PropertyBuilder property = tb.DefineProperty(names[i], PropertyAttributes.None, CallingConventions.HasThis, generics[i], Type.EmptyTypes);
property.SetGetMethod(getter);
}
// ToString()
MethodBuilder toString = tb.DefineMethod("ToString", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.HasThis, typeof(string), Type.EmptyTypes);
toString.SetCustomAttribute(DebuggerHiddenAttributeBuilder);
ILGenerator ilgeneratorToString = toString.GetILGenerator();
ilgeneratorToString.DeclareLocal(typeof(StringBuilder));
ilgeneratorToString.Emit(OpCodes.Newobj, StringBuilderCtor);
ilgeneratorToString.Emit(OpCodes.Stloc_0);
// Equals
MethodBuilder equals = tb.DefineMethod("Equals", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.HasThis, typeof(bool), new[] { typeof(object) });
equals.SetCustomAttribute(DebuggerHiddenAttributeBuilder);
equals.DefineParameter(1, ParameterAttributes.None, "value");
ILGenerator ilgeneratorEquals = equals.GetILGenerator();
ilgeneratorEquals.DeclareLocal(tb);
ilgeneratorEquals.Emit(OpCodes.Ldarg_1);
ilgeneratorEquals.Emit(OpCodes.Isinst, tb);
ilgeneratorEquals.Emit(OpCodes.Stloc_0);
ilgeneratorEquals.Emit(OpCodes.Ldloc_0);
Label equalsLabel = ilgeneratorEquals.DefineLabel();
// GetHashCode()
MethodBuilder getHashCode = tb.DefineMethod("GetHashCode", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.HasThis, typeof(int), Type.EmptyTypes);
getHashCode.SetCustomAttribute(DebuggerHiddenAttributeBuilder);
ILGenerator ilgeneratorGetHashCode = getHashCode.GetILGenerator();
ilgeneratorGetHashCode.DeclareLocal(typeof(int));
if (names.Length == 0)
{
ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4_0);
}
else
{
// As done by Roslyn
// Note that initHash can vary, because
// string.GetHashCode() isn't "stable" for
// different compilation of the code
int initHash = 0;
for (int i = 0; i < names.Length; i++)
{
initHash = unchecked(initHash * (-1521134295) + fields[i].Name.GetHashCode());
}
// Note that the CSC seems to generate a
// different seed for every anonymous class
ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4, initHash);
}
for (int i = 0; i < names.Length; i++)
{
// Equals()
Type equalityComparerT = EqualityComparer.MakeGenericType(generics[i]);
MethodInfo equalityComparerTDefault = TypeBuilder.GetMethod(equalityComparerT, EqualityComparerDefault);
MethodInfo equalityComparerTEquals = TypeBuilder.GetMethod(equalityComparerT, EqualityComparerEquals);
ilgeneratorEquals.Emit(OpCodes.Brfalse_S, equalsLabel);
ilgeneratorEquals.Emit(OpCodes.Call, equalityComparerTDefault);
ilgeneratorEquals.Emit(OpCodes.Ldarg_0);
ilgeneratorEquals.Emit(OpCodes.Ldfld, fields[i]);
ilgeneratorEquals.Emit(OpCodes.Ldloc_0);
ilgeneratorEquals.Emit(OpCodes.Ldfld, fields[i]);
ilgeneratorEquals.Emit(OpCodes.Callvirt, equalityComparerTEquals);
// GetHashCode();
MethodInfo EqualityComparerTGetHashCode = TypeBuilder.GetMethod(equalityComparerT, EqualityComparerGetHashCode);
ilgeneratorGetHashCode.Emit(OpCodes.Stloc_0);
ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4, -1521134295);
ilgeneratorGetHashCode.Emit(OpCodes.Ldloc_0);
ilgeneratorGetHashCode.Emit(OpCodes.Mul);
ilgeneratorGetHashCode.Emit(OpCodes.Call, EqualityComparerDefault);
ilgeneratorGetHashCode.Emit(OpCodes.Ldarg_0);
ilgeneratorGetHashCode.Emit(OpCodes.Ldfld, fields[i]);
ilgeneratorGetHashCode.Emit(OpCodes.Callvirt, EqualityComparerGetHashCode);
ilgeneratorGetHashCode.Emit(OpCodes.Add);
// ToString()
ilgeneratorToString.Emit(OpCodes.Ldloc_0);
ilgeneratorToString.Emit(OpCodes.Ldstr, i == 0 ? string.Format("{{ {0} = ", names[i]) : string.Format(", {0} = ", names[i]));
ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendString);
ilgeneratorToString.Emit(OpCodes.Pop);
ilgeneratorToString.Emit(OpCodes.Ldloc_0);
ilgeneratorToString.Emit(OpCodes.Ldarg_0);
ilgeneratorToString.Emit(OpCodes.Ldfld, fields[i]);
ilgeneratorToString.Emit(OpCodes.Box, generics[i]);
ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendObject);
ilgeneratorToString.Emit(OpCodes.Pop);
}
// .ctor
ilgeneratorConstructor.Emit(OpCodes.Ret);
// Equals()
if (names.Length == 0)
{
ilgeneratorEquals.Emit(OpCodes.Ldnull);
ilgeneratorEquals.Emit(OpCodes.Ceq);
ilgeneratorEquals.Emit(OpCodes.Ldc_I4_0);
ilgeneratorEquals.Emit(OpCodes.Ceq);
}
else
{
ilgeneratorEquals.Emit(OpCodes.Ret);
ilgeneratorEquals.MarkLabel(equalsLabel);
ilgeneratorEquals.Emit(OpCodes.Ldc_I4_0);
}
ilgeneratorEquals.Emit(OpCodes.Ret);
// GetHashCode()
ilgeneratorGetHashCode.Emit(OpCodes.Stloc_0);
ilgeneratorGetHashCode.Emit(OpCodes.Ldloc_0);
ilgeneratorGetHashCode.Emit(OpCodes.Ret);
// ToString()
ilgeneratorToString.Emit(OpCodes.Ldloc_0);
ilgeneratorToString.Emit(OpCodes.Ldstr, names.Length == 0 ? "{ }" : " }");
ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendString);
ilgeneratorToString.Emit(OpCodes.Pop);
ilgeneratorToString.Emit(OpCodes.Ldloc_0);
ilgeneratorToString.Emit(OpCodes.Callvirt, ObjectToString);
ilgeneratorToString.Emit(OpCodes.Ret);
type = tb.CreateType();
type = GeneratedTypes.GetOrAdd(fullName, type);
}
}
}
if (types.Length != 0)
{
type = type.MakeGenericType(types);
}
return type;
}
private static string Escape(string str)
{
// We escape the \ with \\, so that we can safely escape the
// "|" (that we use as a separator) with "\|"
str = str.Replace(#"\", #"\\");
str = str.Replace(#"|", #"\|");
return str;
}
}
So I have a class:
public class MyClass : IMyClass
{
public string foo {get;}
public MyClass bar {get;}
}
And an interface:
public interface IMyClass
{
string foo {get;}
}
And a system to create an emitted type:
private static Type MakeDynamicType<T>() where T : class
{
var myType = GetTypeBuilder();
myType.AddInterfaceImplementation(typeof(T));
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
AddProperty(myType, property, typeof(T));
AddCtor(myType, typeof(T));
return myType.CreateType();
}
private static void AddCtor(TypeBuilder myType, Type inputParamType)
{
var myCtor = myType.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);
var ilGenerator = myCtor.GetILGenerator();
ilGenerator.Emit(OpCodes.Ret);
}
private const MethodAttributes GET_SET_ATTR = MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.HideBySig | MethodAttributes.Virtual;
private static void AddProperty(TypeBuilder myType, PropertyInfo property, Type interfaceType)
{
var myField = myType.DefineField($"m_{property.Name}", property.PropertyType, FieldAttributes.Private);
var myProperty = myType.DefineProperty(property.Name, PropertyAttributes.HasDefault, property.PropertyType,
parameterTypes: null);
var interfaceGetMethod = interfaceType.GetMethod($"get_{property.Name}");
if (interfaceGetMethod != null)
AddGetter(myType, property, myField, myProperty, interfaceGetMethod);
var interfaceSetMethod = interfaceType.GetMethod($"set_{property.Name}");
if (interfaceSetMethod != null)
AddSetter(myType, property, myField, myProperty, interfaceSetMethod);
}
private static void AddGetter(TypeBuilder myType, PropertyInfo property, FieldInfo myField,
PropertyBuilder myProperty, MethodInfo interfaceGetMethod)
{
var myGet = myType.DefineMethod($"get_{property.Name}", GET_SET_ATTR, property.PropertyType,
Type.EmptyTypes);
var getIl = myGet.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, myField);
getIl.Emit(OpCodes.Ret);
myProperty.SetGetMethod(myGet);
myType.DefineMethodOverride(myGet, interfaceGetMethod);
}
private static void AddSetter(TypeBuilder myType, PropertyInfo property, FieldInfo myField,
PropertyBuilder myProperty, MethodInfo interfaceSetMethod)
{
var mySet = myType.DefineMethod($"set_{property.Name}", GET_SET_ATTR, returnType: null,
new[] { property.PropertyType });
var setIl = mySet.GetILGenerator();
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, myField);
setIl.Emit(OpCodes.Ret);
myProperty.SetSetMethod(mySet);
myType.DefineMethodOverride(mySet, interfaceSetMethod);
}
private static TypeBuilder GetTypeBuilder()
{
var myDomain = Thread.GetDomain();
var myAsmName = new AssemblyName("MyDynamicAssembly");
var myAsmBuilder = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.RunAndSave);
var myModBuilder = myAsmBuilder.DefineDynamicModule(myAsmName.Name, myAsmName.Name + ".dll");
return myModBuilder.DefineType("MyDynamicType", TypeAttributes.Public);
}
So now how do I create an instance of my reflected type from an IMyClass type reference to a MyClass object?
public static IEnumerable<T> ToInterfacedObjects<T>(this IEnumerable<T> data) where T : class
{
var myType = MakeDynamicType<T>();
var list = new List<T>();
foreach (var datum in data)
{
list.Add((T)myType.GetValue(datum));//What do I write for GetValue??
}
return list;
}
My goal is to start with an IMyClass which has an underlying type of MyClass, which has both foo and bar, and end with an IMyClass which has an underlying type of an emitted type, which has foo but not bar.
If you've got an implementation for MakeDynamicType<T>, and assuming your interface has only simple {get; set;} properties, wouldn't this be as simple as:
public static IList<T> ToInterfacedObjects<T>(this IEnumerable<T> data) where T : class
{
var myType = MakeDynamicType<T>();
var list = new List<T>();
foreach (var datum in data)
{
var obj = (T)Activator.CreateInstance(myType);
foreach (var pi in typeof(T).GetProperties())
{
var val = pi.GetValue(datum);
pi.SetValue(obj, val);
}
list.Add(obj);
}
return list;
}
I am trying to create a c# class dynamically at run time.
using System;
class Hist
{
private int? _min;
private int? _max;
public int? min
{
get{return _min;}
set {_min = value;}
}
public int? max
{
get{return _max;}
set {_max = value;}
}
}
public class ProcessData
{
private string _id;
private string _source;
private int? _currentValue;
private Hist _hist;
public Hist hist
{
get { return _hist; }
set{ _hist = value; }
}
public string id
{
get {return _id;}
set { _id = value; }
}
public string source
{
get {return _source;}
set { _source = value; }
}
public int? currentValue
{
get {return _currentValue;}
set { _currentValue = value; }
}
public int? min
{
get { return (hist != null) ? hist.min : null; }
}
public int? max
{
get { return (hist != null) ? hist.max : null; }
}
}
But i am unable to do this specifically.
return (hist != null) ? hist.max : null;
i just need is the get method for any of the min or max property of ProcessData class.
My code for above task:
var method = parentType.GetMethod("get_" + propertyName);
getPropMthdBldr = tb.DefineMethod("get_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
propertyType, Type.EmptyTypes);
getIl = getPropMthdBldr.GetILGenerator();
var moveTo = getIl.DefineLabel();
getIl.Emit(OpCodes.Ldarg_0);
getIl.EmitCall(OpCodes.Call, parentGetMethod, Type.EmptyTypes);
getIl.Emit(OpCodes.Brtrue_S, moveTo);
getIl.Emit(OpCodes.Ldloca_S, 0);
getIl.Emit(OpCodes.Initobj, typeof(int?));
getIl.Emit(OpCodes.Ldloc_0);
getIl.Emit(OpCodes.Ret);
getIl.MarkLabel(moveTo);
getIl.Emit(OpCodes.Ldarg_0);
getIl.EmitCall(OpCodes.Call, parentGetMethod,Type.EmptyTypes);
getIl.EmitCall(OpCodes.Callvirt, method,Type.EmptyTypes);
getIl.Emit(OpCodes.Ret);
Problem is that you are trying to use local variable that not declared:
getIl.Emit(OpCodes.Ldloca_S, 0); // load address of local variable with index 0 on stack
getIl.Emit(OpCodes.Initobj, typeof(int?)); // initialize local variable
getIl.Emit(OpCodes.Ldloc_0); // load value of local variable with index 0 on stack
You can define required local variable like this:
var local = getIl.DeclareLocal(typeof(int?));
And your code will be valid, but to improve readability I would advise you to use variable local instead index. It could be done like this:
getIl.Emit(OpCodes.Ldloca_S, local); // load address of local variable on stack
getIl.Emit(OpCodes.Initobj, typeof(int?)); // initialize local variable
getIl.Emit(OpCodes.Ldloc, local); // load value of local variable on stack
P.S. I'll put here code that I've used for generation of classes Hist and ProcessData, may be it could be useful for you in some way, in case if my explanation was not sufficient.
Main logic in this helper for property creation:
public static class TypeBuilderExtensions
{
public static PropertyBuilder CreateProperty<T>(this TypeBuilder builder, string name) => CreateProperty(builder, typeof(T), name);
public static PropertyBuilder CreateProperty(this TypeBuilder builder, Type propertyType, string name)
{
var field = builder.DefineField($"_{name}", propertyType, FieldAttributes.Private);
var getMethodBuilder = builder.DefineMethod($"get_{name}", MethodAttributes.Public, propertyType, Type.EmptyTypes);
var getGenerator = getMethodBuilder.GetILGenerator();
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Ldfld, field);
getGenerator.Emit(OpCodes.Ret);
var setMethodBuilder = builder.DefineMethod($"set_{name}", MethodAttributes.Public, typeof(void), new[] { propertyType });
var setGenerator = setMethodBuilder.GetILGenerator();
setGenerator.Emit(OpCodes.Ldarg_0);
setGenerator.Emit(OpCodes.Ldarg_1);
setGenerator.Emit(OpCodes.Stfld, field);
setGenerator.Emit(OpCodes.Ret);
var propertyBuilder = builder.DefineProperty(name, PropertyAttributes.None, propertyType, Type.EmptyTypes);
propertyBuilder.SetGetMethod(getMethodBuilder);
propertyBuilder.SetSetMethod(setMethodBuilder);
return propertyBuilder;
}
public static PropertyBuilder CreateCalculatedProperty<T>(this TypeBuilder builder, string name, MethodInfo getObject, MethodInfo getObjectProperty) => CreateCalculatedProperty(builder, typeof(T), name, getObject, getObjectProperty);
public static PropertyBuilder CreateCalculatedProperty(this TypeBuilder builder, Type propertyType, string name, MethodInfo getObject, MethodInfo getObjectProperty)
{
var getMethodBuilder = builder.DefineMethod($"get_{name}", MethodAttributes.Public, propertyType, Type.EmptyTypes);
var getGenerator = getMethodBuilder.GetILGenerator();
var label = getGenerator.DefineLabel();
var local = getGenerator.DeclareLocal(propertyType);
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Callvirt, getObject);
getGenerator.Emit(OpCodes.Brtrue, label);
getGenerator.Emit(OpCodes.Ldloca_S, local);
getGenerator.Emit(OpCodes.Initobj, propertyType);
getGenerator.Emit(OpCodes.Ldloc, local);
getGenerator.Emit(OpCodes.Ret);
getGenerator.MarkLabel(label);
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Callvirt, getObject);
getGenerator.Emit(OpCodes.Callvirt, getObjectProperty);
getGenerator.Emit(OpCodes.Ret);
var propertyBuilder = builder.DefineProperty(name, PropertyAttributes.None, propertyType, Type.EmptyTypes);
propertyBuilder.SetGetMethod(getMethodBuilder);
return propertyBuilder;
}
}
Here is class creation itself:
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("TestModule");
var histBuilder = moduleBuilder.DefineType("Hist");
var minProperty = histBuilder.CreateProperty<int?>("min");
var maxProperty = histBuilder.CreateProperty<int?>("max");
var processDataBuilder = moduleBuilder.DefineType("ProcessData");
var histProperty = processDataBuilder.CreateProperty(histBuilder, "hist");
processDataBuilder.CreateProperty<string>("id");
processDataBuilder.CreateProperty<string>("source");
processDataBuilder.CreateProperty<int?>("currentValue");
processDataBuilder.CreateCalculatedProperty<int?>("min", histProperty.GetMethod, minProperty.GetMethod);
processDataBuilder.CreateCalculatedProperty<int?>("max", histProperty.GetMethod, maxProperty.GetMethod);
And finally primitive validation of created classes:
void ValidateProperty(object instance, string name, object value, bool setValue = true)
{
var type = instance.GetType();
var property = type.GetProperty(name);
if (setValue) property.SetValue(instance, value);
var result = property.GetValue(instance);
var equals = property.PropertyType.IsValueType && value != null ? value.Equals(result) : value == result;
if (!equals)
throw new InvalidDataException("Property not valid");
}
var histType = histBuilder.CreateType();
var histInstance = Activator.CreateInstance(histType);
ValidateProperty(histInstance, "min", 12);
ValidateProperty(histInstance, "max", 21);
var processDataType = processDataBuilder.CreateType();
var processDataInstance = Activator.CreateInstance(processDataType);
ValidateProperty(processDataInstance, "hist", histInstance);
ValidateProperty(processDataInstance, "id", "Test!");
ValidateProperty(processDataInstance, "source", "Source#");
ValidateProperty(processDataInstance, "currentValue", 126);
ValidateProperty(processDataInstance, "min", 12, false);
ValidateProperty(processDataInstance, "max", 21, false);
ValidateProperty(processDataInstance, "hist", null);
ValidateProperty(processDataInstance, "min", null, false);
ValidateProperty(processDataInstance, "max", null, false);
The Mongodb C# Driver will not serialize structs/value types. How can this be done?
You can create a custom serializer to handle structs using this code:
public class StructBsonSerializer : IBsonSerializer
{
public void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
{
var fields = nominalType.GetFields(BindingFlags.Instance | BindingFlags.Public);
var propsAll = nominalType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var props = new List<PropertyInfo>();
foreach (var prop in propsAll)
{
if (prop.CanWrite)
{
props.Add(prop);
}
}
bsonWriter.WriteStartDocument();
foreach (var field in fields)
{
bsonWriter.WriteName(field.Name);
BsonSerializer.Serialize(bsonWriter, field.FieldType, field.GetValue(value));
}
foreach (var prop in props)
{
bsonWriter.WriteName(prop.Name);
BsonSerializer.Serialize(bsonWriter, prop.PropertyType, prop.GetValue(value, null));
}
bsonWriter.WriteEndDocument();
}
public object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options)
{
var obj = Activator.CreateInstance(actualType);
bsonReader.ReadStartDocument();
while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
{
var name = bsonReader.ReadName();
var field = actualType.GetField(name);
if (field != null)
{
var value = BsonSerializer.Deserialize(bsonReader, field.FieldType);
field.SetValue(obj, value);
}
var prop = actualType.GetProperty(name);
if (prop != null)
{
var value = BsonSerializer.Deserialize(bsonReader, prop.PropertyType);
prop.SetValue(obj, value, null);
}
}
bsonReader.ReadEndDocument();
return obj;
}
public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
{
return Deserialize(bsonReader, nominalType, nominalType, options);
}
public bool GetDocumentId(object document, out object id, out Type idNominalType, out IIdGenerator idGenerator)
{
throw new NotImplementedException();
}
public void SetDocumentId(object document, object id)
{
throw new NotImplementedException();
}
}
Then, register that serializer for your struct:
BsonSerializer.RegisterSerializer(typeof(MyStruct), new StructBsonSerializer());
Based on above code I manage to adapt it to Mongo Driver version 2.2.3.
public class BasicStructSerializer<T> : StructSerializerBase<T> where T: struct
{
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T value)
{
var nominalType = args.NominalType;
var fields = nominalType.GetFields(BindingFlags.Instance | BindingFlags.Public);
var propsAll = nominalType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var props = new List<PropertyInfo>();
foreach (var prop in propsAll)
{
if (prop.CanWrite)
{
props.Add(prop);
}
}
var bsonWriter = context.Writer;
bsonWriter.WriteStartDocument();
foreach (var field in fields)
{
bsonWriter.WriteName(field.Name);
BsonSerializer.Serialize(bsonWriter, field.FieldType, field.GetValue(value));
}
foreach (var prop in props)
{
bsonWriter.WriteName(prop.Name);
BsonSerializer.Serialize(bsonWriter, prop.PropertyType, prop.GetValue(value, null));
}
bsonWriter.WriteEndDocument();
}
public override T Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
//boxing is required for SetValue to work
var obj = (object)(new T());
var actualType = args.NominalType;
var bsonReader = context.Reader;
bsonReader.ReadStartDocument();
while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
{
var name = bsonReader.ReadName();
var field = actualType.GetField(name);
if (field != null)
{
var value = BsonSerializer.Deserialize(bsonReader, field.FieldType);
field.SetValue(obj, value);
}
var prop = actualType.GetProperty(name);
if (prop != null)
{
var value = BsonSerializer.Deserialize(bsonReader, prop.PropertyType);
prop.SetValue(obj, value, null);
}
}
bsonReader.ReadEndDocument();
return (T)obj;
}
}
Usage:
cm.GetMemberMap(c => c.SomeMemberName).SetSerializer(new BasicStructSerializer<SomeMemberType>());
i write some method for create class and property in run time with Reflection.Emit
my code is:
public class DynamicLibraryProperties
{
public string PropName { get; set; }
public Type PropType { get; set; }
public string DefaultValue { get; set; }
}
public class GenerateDynamicClass
{
public static void GenerateLegacyStructureObject(string libraryName, string className, List<DynamicLibraryProperties> properties)
{
ILGenerator ilgen = default(ILGenerator);
string library = string.Concat(libraryName, ".dll");
AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(libraryName), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule(libraryName, library);
TypeBuilder legacyBuilder = modBuilder.DefineType(string.Concat(libraryName, ".", className), TypeAttributes.Class | TypeAttributes.Public);
//Field Builder - Based on number of months add so many fields
foreach (DynamicLibraryProperties p in properties)
{
FieldBuilder field = legacyBuilder.DefineField(string.Concat("_", p.PropName), p.PropType, FieldAttributes.Private);
PropertyBuilder nameProp = legacyBuilder.DefineProperty(p.PropName, PropertyAttributes.HasDefault, p.PropType, null);
Type[] types = new Type[] { p.PropType };
dynamic typeConvertor = TypeDescriptor.GetConverter(p.PropType);
dynamic defaultValue = typeConvertor.ConvertFromString(p.DefaultValue);
ConstructorInfo ctor = typeof(DefaultValueAttribute).GetConstructor(types);
CustomAttributeBuilder customAttrib = new CustomAttributeBuilder(ctor, new object[] { defaultValue });
nameProp.SetCustomAttribute(customAttrib);
MethodAttributes getAttr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName;
MethodBuilder getNameBuilder = legacyBuilder.DefineMethod(string.Concat("get_", p.PropName), getAttr, p.PropType, Type.EmptyTypes);
ilgen = getNameBuilder.GetILGenerator(); ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldfld, field);
ilgen.Emit(OpCodes.Ret);
MethodBuilder setNameBuilder = legacyBuilder.DefineMethod(string.Concat("set_", p.PropName), getAttr, null, new Type[] { p.PropType });
ilgen = setNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Stfld, field); ilgen.Emit(OpCodes.Ret);
nameProp.SetGetMethod(getNameBuilder);
nameProp.SetSetMethod(setNameBuilder);
}
Type objType = Type.GetType("System.Object");
ConstructorInfo objCtor = objType.GetConstructor(Type.EmptyTypes);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, objCtor);
ilgen.Emit(OpCodes.Ret);
legacyBuilder.CreateType();
asmBuilder.Save(library);
}
}
and use this code like this
List<DynamicLibraryProperties> props = new List<DynamicLibraryProperties>();
props.Add(new DynamicLibraryProperties
{
PropName = "201203", PropType = typeof(float),DefaultValue = "0"
});
props.Add(new DynamicLibraryProperties { PropName = "201204", PropType = typeof(float) ,DefaultValue = "0"});
GenerateDynamicClass.GenerateLegacyStructureObject("test", "test", props);
Now i want create instance of Test class and set value for property value but i don't no how to do it, please help me , thanks all.
You can use Activator.CreateInstance for this, for that you need Type of your test class.
Change the method as below.
public static Type GenerateLegacyStructureObject(string libraryName, string className, List<DynamicLibraryProperties> properties)
{
//your code
Type t = legacyBuilder.CreateType();
asmBuilder.Save(library);
return t;
}
Then use it like this
Type testType = GenerateDynamicClass.GenerateLegacyStructureObject("test", "test", props);
object test = Activator.CreateInstance(testType);
Hope this helps