使用Namespace属性来保证数据契约定义的一致性
为了得到服务所在的数据库中所产生的数据,我在服务中定义了一个自定义的数据契约,用于WCF中的数据交换。不过在客户调用过程中却出现了一个奇怪的问题,明明在服务在得到的数据,可在返回客户的方法中却不能看到数据。通过SvcUtil.exe的生成分析,得到数据契约的的命名空间会带上数据类所在程序集的命名空间,这点与服务契约的方法不一样。如下定义的数据契约:
namespace WCFDataContractTest
{
[DataContract]
struct Product
{
[DataMember]
public string Name;
[DataMember]
public int Number;
}
}
先来看看数据契约类型的定义,这里面定义了两个公共的属性,是为了保证契约的一致性而设的。
[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Class, Inherited=false, AllowMultiple=false)] public sealed class DataContractAttribute : Attribute { // Fields private string name; private string ns; // Properties public string Name{get;set;} public string Namespace{get;set;} ……
}
默认情况下WCF会为我们附加默认的命名空间:
【http://schemas.datacontract.org/2004/07/WCFDataContractTest】
由此可以看出消息契约命名空间规范:通用命名空间+所在程序集的命名空间
即:”http://schemas.datacontract.org/2004/07”+[namesapce];
比较一下服务契约的默认名字空间: “http://tempuri.org/”,它的操作契约的名字空间会再加上接口的名字,不会添加所在程序集的命名空间。
如果要引用服务中定义的数据契约,必须要注意名字空间的使用,否则会造成服务中的数据不能传递到客户端的情况。由此我们可以用以下四种方式来使用服务中所定义的数据契约。
1、 通过在使用VS自带的添加服务引用(SvcUtil.exe)来引用
这种方式在使用IIS托管下是很方便使用的,它可以得到包括数据契约在内的所在服务定义。不过如果是在自托管方式下,就没有那么方便了。这时我们为了得到引用,必须要在服务的绑定中显示设置服务行为的serviceMetadata 节的httpGetEnabled属性,如下所示:
<serviceBehaviors>
<behavior name="mex">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
现在我们启动自托管,就可以得到数据契约的引用了。
2、 将客户代理定义在服务所在的程序集中
将客户代理定义在服务所在的程序集中,这样代理就会使用服务中所定义的同一个数据契约,就不会存在名字空间不一致的问题。
3、 在客户端中自定义数据契约中显式设置名字空间
如前一个例子所示,我们在客户的契约定义中可以作如下设置,这样就可以顺利地使用所定义的数据契约了(服务端不设置名字空间):
namespace Client
{
[DataContract(Name = "Product",
Namespace = "http://schemas.datacontract.org/2004/07/WCFDataContractTest")]
struct Product
{
[DataMember ]
public string Name{get;set;}
[DataMember ]
public int Number{get;set;}
}
}
4、 在服务和客户的数据契约定义中设置同样的自定义的命名空间
这样方式不受WCF自定义数据契约的默认的命名空间的限制,这也是最为方便的一种方式。如我们在服务和代理的契约中同时设置数据契约的Namespace属性:
[DataContract(Namespace="http://www.cnblogs.com/suyan010203")]
总之,在使用数据契约时一定要注意设置名字空间,最好在显示地同时在服务和代理在设置一样的名字空间,这样就可以一致地使用数据契约了,推荐的做法是在WCF中显式地定义自已的名字空间来取代默认的命名空间。(如第4种方式所示)