0

我有一个项目,我不能在我想要序列化的类型上使用序列化属性。一般来说,我可以这样做:

private byte[] Serialize(object value)
{
    var type = value.GetType();
    var typeModel = RuntimeTypeModel.Default.Add(type, false);

    foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
    {
        typeModel.Add(prop.Name);
    }

    using (var stream = new MemoryStream())
    {
        try
        {
            Serializer.Serialize(stream, value);
            return stream.ToArray();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

但是,我有一种类型DynamicEntity(见下文),这对于不会序列化的整个解决方案至关重要。我一直在研究它,但我想不出一种方法来让 RuntimeTypeModel 保存正确的序列化信息。相反,Serialize 不断抛出 InvalidCastException 消息

“无法将‘OpenNETCF.ORM.FieldValue’类型的对象转换为‘System.String’类型。”

以下是相关的类定义:

public class DynamicEntity
{
    public DynamicEntity();

    public string EntityName { get; set; }
    public FieldCollection Fields { get; }
}

public class FieldCollection : IEnumerable<FieldValue>, IEnumerable
{
    public int Count { get; }

    public object this[string fieldName] { get; set; }

    public void Add(string fieldName);
    public void Add(string fieldName, object value);
    public IEnumerator<FieldValue> GetEnumerator();
}

public class FieldValue
{
    public string Name { get; }
    public object Value { get; set; }
}

FieldValue 通常只保存简单的值——数据库字段可能保存的东西。我能够修改上述类的定义(即拥有它们),但我不想强迫该类型的其他消费者反过来必须引用或使用 protobuf。

4

2 回答 2

1

好吧,最后它肯定没有我希望的那么简单,而且它也没有我想要的那么健壮,因为我不得不硬编码可以工作但我正在使用的解决方案的类型有一组有限的类型(无论如何现在),所以在 Marc 的间接帮助下,这一点封装古怪起作用了:

[ProtoContract]
internal class SerializableDynamicEntity
{
    [ProtoMember(1)]
    public string EntityName { get; set; }

    [ProtoMember(2)]
    public List<SerializableFieldValue> Fields { get; set; }

    public SerializableDynamicEntity()
    {
        Fields = new List<SerializableFieldValue>();
    }

    private SerializableDynamicEntity(string name)
        : this()
    {
        EntityName = name;
    }

    public static explicit operator SerializableDynamicEntity(DynamicEntity de)
    {
        var sde = new SerializableDynamicEntity(de.EntityName);

        foreach (var f in de.Fields)
        {
            sde.Fields.Add(SerializableFieldValue.Create(f));
        }

        return sde;
    }

    public static explicit operator DynamicEntity(SerializableDynamicEntity sde)
    {
        var de = new DynamicEntity(sde.EntityName);

        foreach (var f in sde.Fields)
        {
            de.Fields.Add(f.Name, f.UntypedValue);
        }

        return de;
    }
}

[ProtoContract]
[ProtoInclude(3, typeof(SerializableFieldValue<bool>))]
[ProtoInclude(4, typeof(SerializableFieldValue<int>))]
[ProtoInclude(5, typeof(SerializableFieldValue<double>))]
[ProtoInclude(6, typeof(SerializableFieldValue<string>))]
[ProtoInclude(7, typeof(SerializableFieldValue<DateTime>))]
[ProtoInclude(8, typeof(SerializableFieldValue<long>))]
[ProtoInclude(9, typeof(SerializableFieldValue<short>))]
internal abstract class SerializableFieldValue
{
    public static SerializableFieldValue<T> Create<T>(string name, T value)
    {
        return new SerializableFieldValue<T>()
        {
            Name = name,
            Value = value
        };
    }

    public static SerializableFieldValue Create(FieldValue f)
    {
        var type = f.Value.GetType();

        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Boolean:
                return Create(f.Name, (bool)f.Value);
            case TypeCode.Int32:
                return Create(f.Name, (int)f.Value);
            case TypeCode.Double:
                return Create(f.Name, (double)f.Value);
            case TypeCode.String:
                return Create(f.Name, (string)f.Value);
            case TypeCode.DateTime:
                return Create(f.Name, (DateTime)f.Value);
            case TypeCode.Int64:
                return Create(f.Name, (long)f.Value);
            case TypeCode.Int16:
                return Create(f.Name, (short)f.Value);
            default:
                throw new NotSupportedException();
        }
    }

    [ProtoMember(1)]
    public string Name { get; set; }
    public abstract object UntypedValue { get; set; }
}

[ProtoContract]
internal sealed class SerializableFieldValue<T> : SerializableFieldValue
{
    public SerializableFieldValue()
    {
    }

    [ProtoMember(2)]
    public T Value { get; set; }

    public override object UntypedValue
    {
        get { return Value; }
        set { Value = (T)value; }
    }
}

这样,原始的基本代码不需要属性,甚至不需要任何更改,但是需要序列化的特定存储实现可以在内部隐藏这一点。

于 2013-07-09T22:15:54.740 回答
1

Protobuf-net 不太喜欢object- 它想了解架构。这里的一种选择是将 FieldValue 作为具有通用子类的抽象FieldValue<T>,显式提供不同的子类标识符。这可以通过[ProtoInclude]或 通过来完成RuntimeTypeModel。然而,对我来说,消费者的问题在这里并不明显,即属性是否是一个问题。你能澄清一下吗?

于 2013-07-09T18:23:40.123 回答