使用DataContract实现不同类型(bool?和enum)之间的映射

时间:2015-07-28 17:59:29

标签: c# .net datacontract

我正在使用DataContract来序列化/反序列化用户计算机上的对象。所以基本上是一个保存/加载场景。

我的DataContract订单DataMember类型为bool?,现在我需要将其更改为Enum。它是有序的,因为其中一个序列化程序是protobuf-net

现在的样子:

[DataContract]
public class Result
{ 
    [DataMember(Order = 3)]
    public bool? Accepted { get; set; }
}

需要:

public enum ResultDecisionStatus
{
    Accepted, //// map as 'true' for 'bool?'
    Rejected, //// map as 'false' for 'bool?'
    Neutral, //// new
    Unknown //// map as 'null' for 'bool?'
}

[DataContract]
public class Result
{
    [DataMember(Order = 4?)]   //// I assume ordering might have to change
    public ResultDecisionStatus DecisionStatus { get; set; }
}

我实现了以下似乎是 hacky 但似乎有效。我将Accepted保持为相同的顺序并将其更改为private并设置变量以在反序列化时将其映射到新的Enum。这是好设计吗?这感觉很奇怪。

[DataContract]
public class Result
{
    [DataMember(Order = 4)]
    public ResultDecisionStatus DecisionStatus { get; set; }

    [DataMember(Order = 3)]
    private bool? Accepted { get; set; }

    [DataMember(Order = 1003)]
    private bool AcceptedToDecisionStatusMapped { get; set; }

    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
        if (!AcceptedToDecisionStatusMapped)
        {
            switch(Accepted) 
            {
                case true:
                    DecisionStatus = ResultDecisionStatus.Accepted;
                    break;
                case false:
                    DecisionStatus = ResultDecisionStatus.Rejected;
                    break;
                case null:
                    DecisionStatus = ResultDecisionStatus.Unknown;
                    break;
            }
            AcceptedToDecisionStatusMapped = true;
        }
    }
}

问题:有没有正确的方法在不保留旧成员并为每个版本添加额外的映射属性的情况下实现有序DataContracts的类型之间的映射?

2 个答案:

答案 0 :(得分:1)

试试这个:

[DataContract]
public class Result
{ 
    private ResultDecisionStatus? _decisionStatus;

    [DataMember(Order = 3)]
    public bool? Accepted { get; set; }

    [DataMember(Order = 4)]
    public ResultDecisionStatus DecisionStatus 
    { 
        get
        {
            if (_decisionStatus.HasValue)
            {
                return _decisionStatus.Value;
            }
            else if (Accepted.HasValue)
            {
                return Accepted.Value 
                    ? ResultDecisionStatus.Accepted 
                    : ResultDecisionStatus.Rejected;
            }
            else
            {
                return ResultDecisionStatus.Unknown;
            }
        }
        set
        {
            _decisionStatus = value;
        }
    }
}

这基本上应该允许使用旧合同和新协作的数据进行互操作。如果同时使用了34,那会很奇怪,但我认为它会在您的情况下成为其中一个。

<强>更新

请参阅我的其他答案。在您的情况下,由于布尔值可以轻松映射到枚举值,因此更简单。将这个答案留给有人可能没有这种奢侈的情况。

答案 1 :(得分:1)

看起来boolint32在protobuf中兼容,所以你可以这样做:

public enum ResultDecisionStatus
{
    Rejected = 0, // will catch old boolean false values
    Accepted = 1, // will catch old boolean true values
    Neutral = 2 // new
}

[DataContract]
public class Result
{ 
    [DataMember(Order = 3)]
    public ResultDecisionStatus? DecisionStatus { get; set; } 
}

只需使用null代表Unknown