Hello,
at runtime I generate a dynamic assembly, that contains the data model for my application. Every class is annotated with the DataContractAttribute
and every property - with the DataMemberAttribute
. Now if I try to serialize an object, the resulting XML contains only the root node, but no properties. However if I define the exact same class in code - then it works fine.
So far I am using a "User"-object for testing. Here is the "static" implementation:
[DataContract]
private class NonDynamicUser
{
[DataMember]
public Guid Id { get; set; }
[DataMember]
public String Username { get; set; }
[DataMember]
public String Password { get; set; }
}
And here is the "dynamic" one:
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
TypeBuilder userTypeBuilder = moduleBuilder.DefineType(assemblyName + "." + "User", TypeAttributes.Public);
Type attrType = typeof(DataContractAttribute);
userTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(attrType.GetConstructor(Type.EmptyTypes), new object[] { }));
CreateFieldForType(userTypeBuilder, typeof(Guid), "Id");
CreateFieldForType(userTypeBuilder, typeof(String), "Username");
CreateFieldForType(userTypeBuilder, typeof(String), "Password");
Type userType = userTypeBuilder.CreateType();
the void CreateFieldForType(TypeBuilder typeBuilder, Type fieldType, String fieldName)
method basically creates a private field and then a public property (annotated with DataMemberAttribute
) with a public getter and setter.
I create the instance-to-be-serialized again via reflection and this works well with the "dynamic" object as well as the "static" one:
object reflectedUser = Activator.CreateInstance(userType);
//object reflectedUser = Activator.CreateInstance(typeof(NonDynamicUser));
reflectedUser.GetType().GetProperty("Id").SetValue(reflectedUser, Guid.NewGuid(), null);
reflectedUser.GetType().GetProperty("Username").SetValue(reflectedUser, "s7orm", null);
reflectedUser.GetType().GetProperty("Password").SetValue(reflectedUser, "s7orm", null);
And this is how I serialize the object (straightforward stuff):
DataContractSerializer ser = new DataContractSerializer(reflectedUser.GetType());
ser.WriteObject(writer, reflectedUser);
This produces different output, depending whether the dynamic or static user class was used...
Do you have an idea what am I doing wrong?
Update:
Here is the code of the CreateFieldForType
method:
private static void CreateFieldForType(TypeBuilder typeBuilder, Type fieldType, String fieldName)
{
Type attrType = typeof(DataMemberAttribute);
CustomAttributeBuilder attr = new CustomAttributeBuilder(attrType.GetConstructor(Type.EmptyTypes), new object[] { });
FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + fieldName.ToLowerInvariant(), fieldType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(fieldName, PropertyAttributes.HasDefault, fieldType, null);
propertyBuilder.SetCustomAttribute(attr);
MethodAttributes getterAndSetterAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;
MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_" + propertyBuilder.Name, getterAndSetterAttributes, fieldType, Type.EmptyTypes);
ILGenerator getMethodILGenerator = getMethodBuilder.GetILGenerator();
getMethodILGenerator.Emit(OpCodes.Ldarg_0);
getMethodILGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
getMethodILGenerator.Emit(OpCodes.Ret);
MethodBuilder setMethodBuilder = typeBuilder.DefineMethod("set_" + propertyBuilder.Name, getterAndSetterAttributes, null, new Type[] { fieldType });
ILGenerator setMethodILGenerator = setMethodBuilder.GetILGenerator();
setMethodILGenerator.Emit(OpCodes.Ldarg_0);
setMethodILGenerator.Emit(OpCodes.Ldarg_1);
setMethodILGenerator.Emit(OpCodes.Stfld, fieldBuilder);
setMethodILGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getMethodBuilder);
propertyBuilder.SetSetMethod(setMethodBuilder);
}