WCF 从入门到精通——WCF 中应用泛型
第四章 WCF 中应用泛型
1 了 解泛型
面向对象通过继承实现了代码的重用,而泛型则实现了“算法的重用”。比如定义一
种算法“排序”,为了实现尽可能的重用,并不限定该算法操作对象的具体类型,而通过
一个泛型类型来表示,在真正创建泛型对象或调用该方法的时候,才指定其具体的类型。
就泛型的概念来说,这是面向对象的范畴。而数据契约,则属于面向服务的概念,面
向服务是无法理解泛型的(面向服务也没继承、重载的概念)。
2 目 标
本章要达到的目的是在 WCF 服务契约的操作契约中应用泛型,实现类似如下效果:
[OperationContract]
T MethodName(int dataType) where T : EntityBase;
或
[OperationContract]
T MethodName(T t) where T : EntityBase;
正如上一小节所述,WCF 是无法理解泛型的,所以,要实现这里给出的代码效果,必
须变通,变通后效果如下:
[OperationContract]
EntityBase MethodName(int dataType);
或
[OperationContract]
EntityBase MethodName(EntityBase t);
3 具 体示例
3.1 新 建 数 据 模 型 项 目
新建数据模型项目 Zaxx.Abc.WCFModel,并新建解决方案 Zaxx.Abc.WCFAbc,如
图:
刘安全
WCF 从入门到精通
10
WCF 从入门到精通——WCF 中应用泛型
再向数据模型项目 Zaxx.Abc.WCFModel 中添加数据模型基类 Person 及两个数据模
型 Student、Teacher,此时解决方案项目结构如图:
Person.cs 代码:
using System.Runtime.Serialization;
namespace Zaxx.Abc.WCFModel
{
/*********************************************************
* 作 者:刘安全
* 创建时间:2015 年 09 月 22 日 05:03:06
* 功能描述:
*
* 更改历史:
*
* *******************************************************/
///
/// 类 Person:
/// 刘安全 2015 年 09 月 22 日 05:03:06
///
刘安全
WCF 从入门到精通
11
WCF 从入门到精通——WCF 中应用泛型
[KnownType(typeof (Student))]
[KnownType(typeof (Teacher))]
public class Person
{
///
/// 名字
/// 刘安全 2015 年 09 月 22 日 05:04:15
///
public string Name { get; set; }
///
/// 年龄
/// 刘安全 2015 年 09 月 22 日 05:04:55
///
public int Age { get; set; }
///
/// 介绍
/// 刘安全 2015 年 09 月 22 日 05:05:16
///
public virtual string Introduce()
{
return $"Person:{Name},{Age}岁。";
}
}
}
Student.cs 代码:
namespace Zaxx.Abc.WCFModel
{
/*********************************************************
* 作 者:刘安全
* 创建时间:2015 年 09 月 22 日 05:06:26
* 功能描述:
*
* 更改历史:
*
* *******************************************************/
///
/// 类 Student:
/// 刘安全 2015 年 09 月 22 日 05:06:26
///
public class Student : Person
{
///
/// 分数
/// 刘安全 2015 年 09 月 22 日 05:06:55
///
public int Score { get; set; }
刘安全
WCF 从入门到精通
12
WCF 从入门到精通——WCF 中应用泛型
///
/// 介绍
/// 刘安全 2015 年 09 月 22 日 05:07:16
///
public override string Introduce()
{
return $"Student:{Name},{Age}岁,{Score}分。";
}
}
}
Teacher.cs 代码:
namespace Zaxx.Abc.WCFModel
{
/*********************************************************
* 作 者:刘安全
* 创建时间:2015 年 09 月 22 日 05:07:50
* 功能描述:
*
* 更改历史:
*
* *******************************************************/
///
/// 类 Teacher:
/// 刘安全 2015 年 09 月 22 日 05:07:50
///
public class Teacher : Person
{
///
/// 工资
/// 刘安全 2015 年 09 月 22 日 05:08:46
///
public double Salary { get; set; }
///
/// 介绍
/// 刘安全 2015 年 09 月 22 日 05:09:26
///
///
System.String.
public override string Introduce()
{
return $"Teacher:{Name},{Age}岁,{Salary}元/月。";
}
}
}
3.2 新 建 WCF 服 务 库
新建 WCF 服务库 Zaxx.Abc.WCFServiceLibrary,此时解决方案项目结构如图:
刘安全
WCF 从入门到精通
13
WCF 从入门到精通——WCF 中应用泛型
IService1.cs 代码:
using System.ServiceModel;
using Zaxx.Abc.WCFModel;
namespace Zaxx.Abc.WCFServiceLibrary
{
/*********************************************************
* 作 者:刘安全
* 创建时间:2015 年 09 月 22 日 05:10:17
* 功能描述:
* 此示例中定义的服务契约中给出的 Person Test(Person p);
* 等效于: T Test(T p) where T: Person;
* 但不能用 T Test(T p) where T: Person; 因为会抛异常类似如下信息:
* 类型“System.Collections.Generic...[T]”无法作为架构类型被导出,因为
它是一种开放的泛型类型。泛型类型仅可以在其所有的泛型参数类型均为实际类型时才可以被导出
* 更改历史:
*
* *******************************************************/
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的
接口名“IService1”。
[ServiceContract]
public interface IService1
{
[OperationContract]
Person Test(Person p); // 等效于: T Test(T p) where T: Person;
}
}
Service1.cs 代码:
using Zaxx.Abc.WCFModel;
namespace Zaxx.Abc.WCFServiceLibrary
{
刘安全
WCF 从入门到精通
14
WCF 从入门到精通——WCF 中应用泛型
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的
类名“Service1”。
public class Service1 : IService1
{
///
/// 当前方法等效于: T Test(T p) where T: Person
/// 演示了方法中可以接收与返回泛型 Person
/// 刘安全 2015-09-22 05:16:27
///
///
The p.
///
Person.
public Person Test(Person p)
{
if (p.Name == "1")
return new Student {Age = 12, Name = "学生", Score = 90};
if (p.Name == "2")
return new Teacher {Age = 35, Name = "老师", Salary = 4000};
return new Person {Age = 12, Name = "test"};
}
}
}
3.3 新 建 WCF 客 户 端
新建一个控制台项目 Zaxx.Abc.WCFClient 作为 WCF 客户端,此时解决方案项目结
构如图:
添加数据模型的引用,如图:
刘安全
WCF 从入门到精通
15
WCF 从入门到精通——WCF 中应用泛型
添加服务引用
Program.cs:
using System;
using Zaxx.Abc.WCFClient.ZaxxServiceReference;
using Zaxx.Abc.WCFModel;
namespace Zaxx.Abc.WCFClient
{
internal class Program
{
private static void Main(string[] args)
{
Service1Client client = new Service1Client();
Person ps = new Person();
do
{
刘安全
WCF 从入门到精通
16
WCF 从入门到精通——WCF 中应用泛型
Console.WriteLine("输入 y,回车退出。");
ps.Name = Console.ReadLine();
Person p = client.Test(ps);
Console.WriteLine(p.Introduce());
} while (ps.Name != "y");
}
}
}
3.4 运 行 WCF 客 户 端 测 试
运行 WCF 客户端测试会发现:
输入 1 按回车时,控制台上输出: Student:学生,12 岁,90 分。
输入 2 按回车时,控制台上输出: Teacher:老师,35 岁,4000 元/月。
输入 3 按回车时,控制台上输出: Person:test,12 岁。
3.5 注 意 事 项
数据模型的基类定义时,要用[KnownType(typeof (…))]把子类给标出来,不然在
WCF 服务中输出子类对象时,会抛异常类似如下:
接收对 https://xx.com/xx.svc 的 HTTP 响应时发生错误。这可能是由于服务终
结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求
上下文(可能由于服务关闭)所致。有关详细信息,请参见服务器日志。
出现这样的异常是因为 WCF 服务知道 Person,但不知道 Student 与 Teacher。
WCF 客户端若未引用数据模型,只引用服务时,虽然有数据模型项目中各模型同
名的类,但这些类是服务中所用到的且被导入到添加服务引用时所给的命名空间
里的类,并不是数据模型里的类。
若本示例中创建 WCF 客户端时未引用数据模型项目,则 Person、Student、
Teacher 就是命名空间 Zaxx.Abc.WCFClient.ZaxxServiceReference 中的类,而
不是 Zaxx.Abc.WCFModel 中的类。
不同的命名空间中类的对象,无法实现转换。
WCF 客户端若要引用数据模型与服务,则须先引用数据模型,后引用服务,不然
Person、Student、Teacher 会同时出现在 Zaxx.Abc.WCFModel 中与
Zaxx.Abc.WCFModel 中。
若引用顺序反了,则可在引用的服务上右击“更新服务引用”即可。
刘安全
WCF 从入门到精通
17