DataContract 支持继承方式,但和我们前面所提到的 ServiceContract 一样,有点小小的问题。看下面的例子。
(为显示方便,以下代码有删减。)
public class Data
{
[DataMember]
public int X;
}
[DataContract]
public class Data2 : Data
{
[DataMember]
public int Y;
}
[ServiceContract]
public interface IMyService
{
[OperationContract]
void Test(Data d);
}
客户端代理
// <auto-generated>
// 此代码由工具生成。
// 运行库版本:2.0.50727.42
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace ConsoleApplication1.localhost
{
[GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[DataContractAttribute(Namespace = "...")]
[SerializableAttribute()]
public partial class Data : object, IExtensibleDataObject
{
}
}
从 客户端生成的代码来看,我们显然不能使用 Data2。怎样才能使用 Data2 呢?将契约 IMyService 的 Test 方法参数改成 "Data2 d"?当然不行,这样一来 Data 就不能用了,更不要说什么多态了。这时候就要使用另外一个特性 —— ServiceKnownTypeAttribute。继续看我们修改后的代码。
public class Data
{
[DataMember]
public int X;
}
[DataContract]
public class Data2 : Data
{
[DataMember]
public int Y;
}
[ServiceContract]
[ServiceKnownType(typeof(Data2))]
public interface IMyService
{
[OperationContract]
void Test(Data d);
}
客户端代理
// <auto-generated>
// 此代码由工具生成。
// 运行库版本:2.0.50727.42
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace ConsoleApplication1.localhost
{
[GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[DataContractAttribute(Namespace = "...")]
[SerializableAttribute()]
[KnownTypeAttribute(typeof(Data2))]
public partial class Data : object, IExtensibleDataObject
{
}
[GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[DataContractAttribute(Namespace = "...")]
[SerializableAttribute()]
public partial class Data2 : Data
{
}
}
非常好,Data2 出现了,而且它也继承自 Data,我们写个完整的例子,看看是否是我们所设想的多态。
服务器端代码
public class Data
{
[DataMember]
public int X;
}
[DataContract]
public class Data2 : Data
{
[DataMember]
public int Y;
}
[ServiceContract]
[ServiceKnownType(typeof(Data2))]
public interface IMyService
{
[OperationContract]
void Test(Data d);
}
public class MyServie : IMyService
{
public void Test(Data d)
{
Console.WriteLine(d.GetType());
Console.WriteLine(d.X);
Console.WriteLine((d as Data2).Y);
}
}
客户端代码
{
Data2 d = new Data2();
d.X = 1234;
d.Y = 5678;
client.Test(d);
}
执行后服务器端输出:
Data2
1234
5678
ServiceKnownTypeAttribute 还可以直接用在 Method 上,另外还要注意我们将 ServiceKnownTypeAttribute 放在服务契约上,而不是其他什么地方。
AttributeTargets.Method |
AttributeTargets.Class,
AllowMultiple = true)]
public sealed class ServiceKnownTypeAttribute : Attribute
{
}
相应地,我们还可以直接在配置文件中添加 ServiceKnownTypeAttribute,这样可以避免修改服务器端代码(也许吧?前提是 Data2 要放到另外一个程序集中)。
看另外一种情形。
{
int X { get; set; }
}
[DataContract]
public class Data : IData
{
private int x;
[DataMember]
public int X
{
get { return x; }
set { x = value; }
}
}
[ServiceContract]
public interface IMyService
{
[OperationContract]
void Test(IData d);
}
麻烦依旧,看看自动生成的客户端代理。
// <auto-generated>
// 此代码由工具生成。
// 运行库版本:2.0.50727.42
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace ConsoleApplication1.localhost
{
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[ServiceContractAttribute(ConfigurationName = "ConsoleApplication1.localhost.IMyService")]
public interface IMyService
{
[OperationContractAttribute(Action = "http://tempuri.org/IMyService/Test", ReplyAction = "...")]
void Test(object d);
}
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IMyServiceChannel : IMyService, IClientChannel
{
}
[DebuggerStepThroughAttribute()]
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class MyServiceClient : ClientBase<IMyService>, IMyService
{
public void Test(object d)
{
base.Channel.Test(d);
}
}
}
没有 IData,没有 Data,取而代之的是 object。这意味着很大的麻烦,不是吗?还是让 ServiceKnownTypeAttribute 出来吧。
public interface IMyService
{
[OperationContract]
[ServiceKnownType(typeof(Data))]
void Test(IData d);
}
更 新后的客户端代理虽然出现了 Data,但 IData 依然不见踪影,Test 方法参数依然是 "object d"。查阅了 MSDN 的一些说明,原因可能是 "the interface itself will not be included in the exported metadata"。不过这并不妨碍我们调用服务,看来 DataContract 不支持 Interface 也是有些原因的。
// <auto-generated>
// 此代码由工具生成。
// 运行库版本:2.0.50727.42
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace ConsoleApplication1.localhost
{
[GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[DataContractAttribute(Namespace = "...")]
[SerializableAttribute()]
public partial class Data : object, IExtensibleDataObject
{
}
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[ServiceContractAttribute(ConfigurationName = "ConsoleApplication1.localhost.IMyService")]
public interface IMyService
{
[OperationContractAttribute(Action = "http://tempuri.org/IMyService/Test", ReplyAction = "...")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(ConsoleApplication1.localhost.Data))]
void Test(object d);
}
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IMyServiceChannel : IMyService, IClientChannel
{
}
[DebuggerStepThroughAttribute()]
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class MyServiceClient : ClientBase<IMyService>, IMyService
{
public void Test(object d)
{
base.Channel.Test(d);
}
}
}