我想知道是否有任何方法可以在不使用 [DataContract] 和 [DataMember] 注释的情况下定义 WCF Contract 类。原因是我们目前拥有的领域模型相当干净,所以我们希望保持这种方式。这里的最佳做法是什么?创建一个传输对象并将域模型对象复制到一个传输对象(具有所需的注释并且合同是否在客户端和服务器之间传输)?或者以某种方式不注释对象模型并以不同的方式指定合同。
4 回答
如果您不向您的类添加任何序列化属性,并将其用作 WCF 服务协定方法的一部分,那么 WCF 将使用默认的序列化规则来生成数据协定。这意味着该类将隐式变为 a[DataContract]
每个同时具有a和访问器的公共属性将隐式变为 a 。get
set
[DataMember]
唯一需要应用属性的时候是如果您想覆盖默认行为,例如隐藏某些属性、应用名称空间等。无论如何,这样做通常被认为是一种好习惯,因为依赖默认行为可能会给您带来麻烦之后。(它还明确表明您的类是供 WCF 使用的)。但这并不是严格要求的,只要默认行为满足您的需求即可。
回应您的跟进:
据我所知,没有完全外部的方法来改变DataContractSerializer
给定类的序列化行为;每个选项至少需要对被序列化的类进行某种程度的归属。正如@Yair Nevet 在下面描述的,我将现有域对象转换为数据契约的首选方法是MetadataType
属性。
或者,您可以通过执行问题中的建议来绕过整个问题:不要序列化域对象,而是创建自定义 DTO 对象并序列化它们。例如,每当我使用实体框架时,我都倾向于这样做,因为对它们进行序列化可能会很棘手。如果您的域对象有很多内置的行为,这也是一个很好的方法——您可以清楚地区分“传递的数据”与“参与我的业务逻辑的对象”。
您通常会得到大量冗余代码,但它确实实现了对现有对象进行零更改的目标。
您可以使用MetadataType
属性和元数据模型类来将注释与模型分开。
例如:
[MetadataType(typeof(MyModelMetadata))]
public class MyModel : MyModelBase {
... /* the current model code */
}
[DataContract]
public class MyModelMetadata {
[DataMember]
public string Name { get; set; }
}
WCF 能够在没有属性的情况下序列化您的对象。这些属性允许自定义。例如,这两个类将通过以下方式进行相同的序列化DataContractSerializer
:
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
[DataContract]
public class Customer
{
[DataMember] public string FirstName { get; set; }
[DataMember] public string LastName { get; set; }
}
值得一提的是,你真的应该用属性标记你的类。它们并不像你想象的那么“混乱”。它实际上会让你在未来免于头痛。例如:
[DataContract(Name = "Customer")]
public class Customer
{
[DataMember(Name = "FirstName")]
public string FirstName { get; set; }
[DataMember(Name = "LastName")]
public string LastName { get; set; }
}
在前面的代码示例中,我明确设置了类和成员的名称。这将允许我在不破坏消费者代码的情况下重构名称。因此,如果有人决定将我的类命名为CustomerDetail而不是Customer,我仍然可以将名称保留为Customer,以便我的服务的消费者继续工作。
你总是可以使用 DTO。创建一个单独的类,其中包含序列化对象所需的一切。然后将您的域模型投影到 DTO。您可以使用 AutoMapper 之类的工具来简化此过程。
关于性能
除非每个类有数百个、可能数千个或对象或大量属性,否则与 DTO 相互转换的行为可能不会有太多的性能开销。
如果您使用的是 EF 之类的东西,并且没有序列化每个属性,您甚至可以通过将 EF 查询直接投射到 DTO 上来减少一些开销。
这是一个戏剧性的案例,但我有(设计不佳的)数据库模型,每种类型有 50 多个属性。通过更改为仅具有我关心的 10-15 个属性的 DTO,我几乎可以将 WCF 服务的性能提高一倍。