在动态程序集中序列化类

时间:2010-08-19 20:43:15

标签: c# serialization

在运行时我生成一个动态程序集,其中包含我的应用程序的数据模型。每个类都使用DataContractAttribute和每个属性进行注释 - 使用DataMemberAttribute。现在,如果我尝试序列化对象,则生成的XML仅包含根节点,但不包含属性。但是,如果我在代码中定义完全相同的类 - 那么它工作正常。 到目前为止,我正在使用“用户”对象进行测试。这是“静态”实现:

[DataContract]
private class NonDynamicUser
{
    [DataMember]
    public Guid Id { get; set; }

    [DataMember]
    public String Username { get; set; }

    [DataMember]
    public String Password { get; set; }
}

这是“动态”的:

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();

void CreateFieldForType(TypeBuilder typeBuilder, Type fieldType, String fieldName)方法基本上创建一个私有字段,然后使用公共getter和setter创建一个公共属性(用DataMemberAttribute注释)。

我通过反射再次创建要被序列化的实例,这适用于“动态”对象以及“静态”对象:

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);

这就是我如何序列化对象(直截了当的东西):

DataContractSerializer ser = new DataContractSerializer(reflectedUser.GetType());
ser.WriteObject(writer, reflectedUser);

这会产生不同的输出,具体取决于是否使用了动态或静态用户类...

你知道我做错了什么吗?

更新 以下是CreateFieldForType方法的代码:

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 个答案:

答案 0 :(得分:1)

将模块名称更改为与程序集/ dll名称相同允许我在Reflector中查看该类:

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

我创建了自己的CreateFieldForType方法版本,它对我有用。这是我确定我正确生成类型的方式:

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));

我选择了刚刚发布的方法,并根据我的代码运行它。它没用。然后我慢慢用我创建的代码替换它。我发现破坏你的代码的行(使我的验证码停止工作)是这一行:

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

我删除MethodAttributes.Virtual标志后,代码的工作方式与NonDynamicUser相同。

我建议您生成的程序集正常工作,以便您可以在反射器中看到生成的类。它将使得进一步修改更容易(例如,您可以添加虚拟关键字)。