动态忽略数据成员的序列化

13
我们有一个现有的WCF服务,其中使用了几个DataContracts。我们想要根据设备修改序列化,因此当从移动设备访问时,服务应该只序列化一些重要的数据成员(而不是全部)。
这里有两个选项:
1.为不同类型的设备创建单独的操作和数据协定。 2.深入实际的xml序列化,并基于设备禁止创建不必要的元素。
我们不想选择第一种选项,因为它会在未来引入大量冗余代码问题。
研究表明,我们需要使用IXmlSerializable并覆盖readXML()和writeXML()方法。但与此同时,我在某个地方看到过DataContract和IXmlSerializable不应该一起使用。
非常感谢任何关于如何深入实际序列化的示例。
[DataContract]
public class TokenMessage
{
    string tokenValue;
    string extraValue;
    [DataMember]
    public string Token
    {
        get { return tokenValue; }
        set { tokenValue = value; }
    }
    [DataMember]
    public string Extra 
    {
        get { return extraValue; }
        set { extraValue = value; }
    }
}
现在当我从移动设备访问返回典型TokenMessage数据协定的服务时,我不希望"Extra"数据成员被序列化,即当我向操作协定提供不同参数时,它应该能够序列化一些/所有数据成员(取决于操作)。请注意,暂时忽略设备检测部分。假设我们在操作协定中有一个参数,可以帮助我们识别设备。

使用接口并返回接口而不是类。 - Dalorzo
4个回答

3
我不确定@Pranav Singh的答案的某个变体是否更好,但这不是你的问题...
正如您在评论中提到的,.NET中的属性是静态设计的。这意味着动态添加/删除[DataMember]不是一个好选择。虽然有可能,例如使用Reflection.Emit来重新创建具有元数据更改的实例(请参见Can attributes be added dynamically in C#?的所有答案),但所有这些方法都很复杂。
我看到两个合理的选择:

1) 为服务实现一个IParameterInspectorAfterCall()方法中,您可以检查和修改返回给客户端的参数,在它们被序列化之前。需要使用反射来动态确定参数类型并设置它们的值,但这并不复杂。这是更好的设计,可以在许多契约或服务中重复使用行为。Carlos Figueira's blog是WCF扩展示例的最佳来源。

2) 使用 [OnSerializing][OnSerialized] 事件。 在 [DataContract] 中,您可以在序列化期间暂时更改属性返回的内容。实际上,这些事件是为了启用初始化,因此这种解决方案有点像黑客方式。这种解决方案也不是线程安全的。但它确实将代码限制在 DataContract 类中,并快速解决了问题(我认为您正在寻找快速解决方案)。

解决方案 #2 可能如下所示:

[DataContract]
public class TokenMessage
{
    string tokenValue;
    string extraValue;

    bool enableExtraValue = true;

    [DataMember]
    public string Extra 
    {
        get { 
                if (enableExtraValue) 
                      return extraValue;
                return null; 
            }
        set { extraValue = value; }
    }

    [OnSerializing()]
    internal void OnSerializingMethod(StreamingContext context)
    {
        enableExtraValue = false;
    }

    [OnSerialized()]
    internal void OnSerializedMethod(StreamingContext context)
    {
        enableExtraValue = true;
    }
}

方案#2是一个快速解决方法(我认为这就是您所寻找的)。

方案#1是更好的设计。


1
有一种方法,但我认为这将需要生成额外的DataContract,但仍然不需要为不同类型的设备创建单独的操作和数据合同。它可以是运行时多态性的经典实现。我只是提供了一个思路:
假设您有一个通用的DataContract,如下所示:
[DataContract]
[KnownType(typeof(Extra))]
[KnownType(typeof(Extra2))]
public class TokenMessage
{
    string tokenValue;
    string extraValue;
    [DataMember]
    public string Token
    {
        get { return tokenValue; }
        set { tokenValue = value; }
    }

}

其他设备特定的合同可以像这样继承TokenMessage作为基类:
[DataContract]
public class Extra:TokenMessage
{
  [DataMember]
  public string Extra 
  {
    get ;set;
  }
}

[DataContract]
public class Extra2:TokenMessage
{
  [DataMember]
  public string Extra2 
  {
    get ;set;
  }
}

现在,在运行时,正如您所说,您可以在操作契约中设置一个参数,这有助于我们识别设备。例如,基于设备类型,您可以实例化带有派生类的基类,例如:

TokenMessage tm= new Extra();

或者

TokenMessage tm= new Extra2();

在运行时,您将决定哪个设备合同将成为通用响应的一部分。
注意:添加“KnownType”将为基类中所有已知类型生成单独的xsd文件,在运行时保存数据序列化,因为这应该取决于实际选择的继承。

1

1
[IgnoreDataMember]是静态的..我希望我的服务在运行时忽略一些数据成员 - Surya KLSV

-2

你的意思是说 - “添加ShouldSerializeToken和ShouldSerializeExtra属性,然后将它们设置为false?” WCF会自动检测到吗? - Surya KLSV
以上方法没有成功 :( - Surya KLSV
2
ShouldSerialize 是与 WPF 相关的,我不知道它如何帮助你的 WCF 服务。 - Sven Grosen
1
将EmitDefaultValue设置为false并显式设置属性为默认值是阻止数据成员被序列化的一种方法。但是我的类成员不仅仅是基本数据类型。它们有很多自定义类作为属性,这些属性正在被序列化。 - Surya KLSV

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接