关于c#:没有注释的WCF合同

WCF Contracts without the annotations

本问题已经有最佳答案,请猛点这里访问。

我想知道是否有任何方法可以在不使用[datacontract]和[datamember]注释的情况下定义WCF契约类。原因是我们目前拥有的域模型相当干净,所以我们希望保持这种方式。这里的最佳实践是什么?创建一个传输对象并将域模型对象复制到一个传输对象中(该对象具有所需的注释,并且契约是否在客户端和服务器之间传输)?或者不以其他方式注释对象模型和指定契约。


如果不向类添加任何序列化属性,并将其用作WCF服务约定方法的一部分,则WCF无论如何都将使用默认序列化规则生成数据约定。这意味着类将隐式地成为[DataContract],每个既有get又有set访问器的公共财产将隐式地成为[DataMember]

您唯一需要应用这些属性的时间是您是否要重写默认行为,例如隐藏一些属性、应用名称空间等。通常认为这样做是一种良好的做法,因为依赖默认行为可能会在以后给您带来麻烦。(它还明确表示您的类是为WCF使用的)。但是,只要默认行为满足您的需要,就不必严格要求它。

针对您的后续行动:

据我所知,没有完全外部的方法来更改给定类的DataContractSerializer的序列化行为;每个选项都要求对要序列化的类至少有某种程度的属性。正如@yair nevet下面所描述的,我将现有域对象转换为数据契约的首选方法是MetadataType属性。

或者,您可以通过执行您在问题中建议的操作来绕过整个问题:不要序列化域对象,而是创建自定义DTO对象并将其序列化。例如,每当我使用实体框架时,我都倾向于这样做,因为序列化那些框架可能很困难。如果您的域对象有许多内置的行为,那么这也是一种很好的方法——您可以清楚地将"数据传递"与"参与我的业务逻辑的对象"分开。

您通常会得到大量的冗余代码,但它确实实现了对现有对象零更改的目标。


可以使用MetadataType属性和元数据模型类,以便将注释与模型分开。

例如:

1
2
3
4
5
6
7
8
9
10
[MetadataType(typeof(MyModelMetadata))]
public class MyModel : MyModelBase {
  ... /* the current model code */
}

[DataContract]
public class MyModelMetadata {
    [DataMember]
    public string Name { get; set; }
}


你可以一直使用DTO。创建一个单独的类,该类包含序列化对象所需的所有内容。然后将您的域模型投影到DTO。您可以使用automapper之类的工具来简化这个过程。

关于绩效

除非每个类有成百上千个对象或大量属性,否则转换DTO和从DTO转换的操作可能没有那么大的性能开销。

如果您使用的是像ef这样的东西,并且没有序列化每个属性,那么您甚至可以通过将ef查询直接投影到DTO来减少一些开销。

这是一个戏剧性的例子,但是我有(设计不好的)数据库模型,每种类型有50多个属性。通过改为只有我关心的10-15属性的DTO,我几乎可以将WCF服务的性能提高一倍。


WCF能够序列化没有属性的对象。这些属性允许定制。例如,这两个类将由DataContractSerializer以相同的方式序列化:

1
2
3
4
5
6
7
8
9
10
11
12
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; }
}

值得一提的是,您确实应该用属性标记您的类。它们不像你想象的那么"杂乱"。它实际上会在将来帮你避免头痛。例如:

1
2
3
4
5
6
7
8
9
[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,以便我的服务的消费者继续工作。