views:

37

answers:

1

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);
}
+1  A: 

Changing the module name to be the same as the assembly/dll name allowed me to see the class in Reflector:

ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("blah.dll");

I created my own version of your CreateFieldForType method, and it was working for me. Here is the way I determined that I generated the type correctly:

userInstance.GetType().GetProperty("Id").SetValue(userInstance, Guid.NewGuid(), null);
userInstance.GetType().GetProperty("Username").SetValue(userInstance, "Test1", null);
userInstance.GetType().GetProperty("Password").SetValue(userInstance, "Test2", null);

MemoryStream stream = new MemoryStream();
DataContractSerializer ser = new DataContractSerializer(userInstance.GetType());
ser.WriteObject(stream, userInstance);

stream.Seek(0, SeekOrigin.Begin);
object reSerialized = ser.ReadObject(stream);
foreach (var property in reSerialized.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
    Console.WriteLine("{0}: {1}", property.Name, property.GetValue(reSerialized, null));

I took the method you just posted and ran it against my code. It didn't work. I then slowly replaced it with the code I created. I found the line that broke your code (made my verification code stop working) was this line:

MethodAttributes getterAndSetterAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;

As soon as I removed the MethodAttributes.Virtual flag, the code worked the same as NonDynamicUser.

I suggest you get your generated assembly working so that you can see the generated class in reflector. It'll make verifying further modifications much easier (so you can add the virtual keyword, for example).

Merlyn Morgan-Graham
Wow, I feel stupid :) I added the virtual flag to test some proxy libraries and then forgot to remove it... Thanks a lot! And your tip with the naming of the dynamic module is priceless!
s7orm
@s7orm: Glad I could help. Cheers!
Merlyn Morgan-Graham