WCF:属性与成员的 DataMember 属性

Posted

技术标签:

【中文标题】WCF:属性与成员的 DataMember 属性【英文标题】:WCF: DataMember attribute on property vs. member 【发布时间】:2010-10-08 02:12:40 【问题描述】:

在wcf中,在属性上应用DataMember属性有什么区别

private int m_SomeValue;

[DataMember]  
public int SomeValue 
  get ...
  set ...

而不是成员变量

[DataMember]  
private int m_SomeValue;

public int SomeValue 
  get ...
  set ...

?

【问题讨论】:

您不需要将私有变量设为数据成员。 【参考方案1】:

一般来说,您应该倾向于在属性上应用 DataMember 属性,而不是在私有字段上。将属性应用于字段的唯一原因是该属性是只读的(即它没有设置器)。

【讨论】:

为什么我们需要这样做? 默认情况下,WCF 中的所有序列化都是双向的。这意味着框架需要能够读取和写入所有数据元素。因此,如果您将具有只读属性的现有类型修改为可序列化,则可能必须将属性添加到字段。不过,这种情况很少见。 如果您的属性的 getter/setter 有任何可能改变其值的代码,将 DataMemberAttribute 放在字段上也很有用。 @rossisdead 除非这可能是预期的行为。【参考方案2】:

只要使用Name 标记,无论使用字段还是属性,合约都是相同的。

[DataMember(Name="SomeValue")]
private int m_SomeValue;

但是,访问私有成员可能会出现一些权限问题,尤其是在 silverlight 和 CF 上 - 在这种情况下,我建议使用公共属性作为数据成员。实际上,除非我有充分的理由,否则我倾向于总是使用属性...

【讨论】:

我完全不同意你的观点,马克,看看我的回答,我想听听你的回复***.com/a/8442768/235715【参考方案3】:

您可能希望将字段而不是属性标记为 DataMember 有充分的理由。

更多详情请查看:http://blog.walteralmeida.com/2010/05/wcf-and-datacontract-serialization-internals-and-tips-.html

顺便说一句:ContractSerializers 将序列化任何具有 DataMemberAttribute 的私有字段只有在完全信任环境中运行时。不能在部分信任下工作(查看上面列出的博客以获取解决方案)

【讨论】:

【参考方案4】:

此决定取决于您的 WCF 服务的使用情况:

    您拥有的 .NET 系统使用的内部服务,它们共享相同的域模型。 不同平台使用的外部服务,不共享相同的域模型。

案例 1。

序列化 - 是保持对象状态的过程。 C# 中对象的状态由它的数据字段表示。

C# 中的属性本质上是方法,用于操作对象的状态。使用它们可能会导致反序列化的不同对象状态,因为设置属性的顺序可能会影响其最终数据状态。其他因素也可能导致不正确的状态反序列化,例如方法(属性集)依赖于某些正在变化的上下文,例如当前的 DateTime。

你可能会说封装呢?我不希望我的对象处于无效状态,我必须进行验证检查、对象图完整性检查等。是的,你应该这样做,所以我们将 DataMember 属性放在 props 上?没有。

这里的问题是很多人混合了两种不同的东西,DTO(数据传输对象,WCF 合同)和域实体。您需要确保您收到的数据与发送的数据完全相同,然后确保您可以从该数据构造有效的域实体。实现这一点的最佳方法是为 DTO 使用单独的类,并从中构造域实体。

但是大多数程序员都很懒惰,他们喜欢用 DataMemeber 属性简单地装饰 Domain Entity。在这种情况下,决定 Field 或 Prop 取决于您的验证逻辑在哪里,如果您的验证逻辑隐藏在 Set 方法中,您将不得不使用 Props,如果它是外部的,您应该使用 Fields,并在反序列化后验证您的域实体。

附:我认为相同的规则适用于任何序列化过程,例如数据库持久性。

另外,我想提一下 Silverlight 不能序列化\反序列化私有字段,因为你不能使用反射从外部访问它们,你必须将它们设为私有并使用 InternalsVisibleToAttribute。

案例 2。

这很难。这里的主要焦点是互操作性。在这种情况下,99.9% 的情况下您将拥有单独的 DTO 类,并且很可能有很多不同版本的它们来支持旧客户端。将 DataMembers 属性放在此处并不重要,因为您使用的是 DTO。我不会费心解释这种情况,因为在这种大规模系统上工作的开发人员通常很有经验,他们不会费心去阅读 SO。

【讨论】:

当涉及到平台绑定、类型绑定的序列化 - 那么是的!这些领域确实代表了国家。我在想BinaryFormatter 等等。但是,就可互操作的数据片段而言,接收类型相同(可能是 mex/wsdl 生成的,也可能是完全不同的平台) ,我们应该不知道(或依赖于)这些字段。是的,验证仍然很重要,并且有相应的机制(以及其他事情,例如回调)。 进一步;序列化的工作不是简单地“保持对象的状态”——它以匹配协议的方式进行。对于 BinaryFormatter,这意味着(默认)“字段”,但对于 WCF ......不是那么多。 data 不是 m_someValue - dataSomeValuem_someValue 是一个实现细节。有一些方法可以处理映射(例如:在字段上使用Name 使m_someValue 显示为SomeValue @MarcGravell 在互操作性方面我同意你的观点,我认为这个主题是一个简单的持久化和恢复状态的过程,但不仅如此。我的回答基本上只适用于两端都有 C# 并且重用相同类的情况。如果我们以 C# 代码作为接收者和 php 代码作为发送者的情况为例,那么情况会有点不同,我需要一些时间来考虑这一点,而不是我想我会更新我的答案。 不过,问题不是“持久化和恢复状态”——而是 WCF。 WCF 按设计和意图是一个平台中立的、与实现无关的通信传输。即使是C#转C#,也要求两端同一个程序集;只是他们使用相同的合同。合同与字段没有任何关系。 @MarcGravell 而且合同也与属性无关。它只关心 XML 元素,你的 DTO 在编程环境中代表这个 Contract,应该没有逻辑。所以它是字段。【参考方案5】:

理论上,只要您保持m_SomeValue 始终等于SomeValue(就像一个简单的getter/setter),什么都没有。除了由 WCF 公开的变量的名称。 (显然,如果您标记m_ 变量,那么您的代理类也将具有相同的m_ 名称。无论您使用公共/受保护/内部/私有字段还是属性,代理类都会生成一个公共属性。

但是,如果您的访问器中有任何特殊逻辑可能会修改返回的值(例如ToUpper()ing 字符串),那么您将返回不同的值。

【讨论】:

【参考方案6】:

就我个人而言,我只会使用该属性并将成员变量一起完全删除。即

[DataMember]
public int SomeValue
 get; set; 

属性会在后台莫名其妙地创建一个成员变量。

【讨论】:

“莫名其妙”?这就是它的本意。也许您的意思是“隐式”? C# 编译器为自动属性生成一个私有的“支持”字段,该字段带有一个错误的名称(以防止与您可能​​已定义的任何其他字段的名称发生冲突)。【参考方案7】:

如果在private int m_SomeValue上添加[DataMember],该成员不能被序列化,所以必须在public int SomeValue上添加。

[DataMember]  
private int m_SomeValue;

public int SomeValue 
  get ...
  set ...

上面的代码如果通过WCF使用,在客户端是无法获取值的。

【讨论】:

DataContractSerializers 将序列化任何具有 DataMemberAttribute 的私有字段。 我同意 rossisdead,任何具有 DataMemberattribute 的私有字段都会被序列化,并通过服务向客户端公开。

以上是关于WCF:属性与成员的 DataMember 属性的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 WCF 的数据合同中使用 DataMember 装饰多个属性?

数据合同和数据成员

数据合同和数据成员

不需要 WCF DataMember 时

在 Web 服务中忽略 DataMember 名称属性

objective-C学习笔记数据成员:属性与实例变量