Silverlight 应用程序常常需要访问后端服务器上的某些数据或功能。例如,如果您正在写入客户管理应用程序,该服务器可能包含您的 Silverlight 应用程序需要检索的客户数据库。如果您正在写入游戏,该服务器可能包含您的 Silverlight 应用程序需要更新的高分数信息。在很多情况下,让 Silverlight 能够使用服务器上的功能的最好方法是创建 Web 服务,如本主题所述。
本主题描述了如何将启用 Silverlight 的服务添加到现有的服务器端项目。一般情况下,这将是 Visual Studio 2010 包含在您的 Silverlight 解决方案中的默认 Web 应用程序。例如,如果正在构建名为 SilverlightApplication1 的应用程序,您将在“解决方案资源管理器”中看到名为 SilverlightApplication1.Web 的 Web 应用程序。但是您可以向任何其他网站或 Web 应用程序添加服务,甚至可以创建一个全新的 WCF 服务应用程序项目。
此过程假定您要在开发环境中使用 Visual Studio 2010。为方便起见,前面的几个步骤将描述在创建 Silverlight 解决方案时如何生成默认的 Web 应用程序。如果要使用另一个项目承载该服务,则可以跳过这几步操作。
有关概述如何通过 Silverlight 客户端访问在此主题中创建的服务的过程,请参见如何:从 Silverlight 访问服务。
将启用 Silverlight 的 WCF 服务添加到现有网站或 Web 应用程序
-
从“文件”菜单中依次选择“新建”、“项目”,然后从“项目类型”中选择 Silverlight,再从“安装了 Visual Studio 的模板”中选择“Silverlight 应用程序”。接受默认“名称”SilverlightApplicaton1,然后单击“确定”。
-
在弹出的“新建 Silverlight 应用程序”向导中,接受已选中的默认选项“在新网站中承载 Silverlight 应用程序”。接受“新建 Web 项目名称”的默认值 SilverlightApplication1.Web,并接受默认的“ASP.NET Web 应用程序项目”作为“新建 Web 项目类型”。然后单击“确定”。
-
若要将启用 Silverlight 的 Windows Communication Foundation (WCF) 服务添加到生成的默认 Web 应用程序中,请右击“解决方案资源管理器”中的 SilverlightApplication1.Web 项目,并选择“添加”,然后选择“新建项”打开“添加新项”窗口。在左侧的“已安装的模板”窗格中,从相关语言组(Visual C# 或 Visual Basic)中选择“Silverlight”,然后从中央窗格中选择“启用了 Silverlight 的 WCF 服务”模板。在底部附近的“名称”框中将服务名称更改为 CustomerService.svc,然后单击该框右侧的“添加”按钮。
-
若要定义和实现
CustomerService
协定,请使用希望服务支持的操作替换CustomerService
类中的DoWork
方法(已在 CustomerService.svc.cs 文件中定义)。在此示例中,我们将添加两项操作以支持 Silverlight 客户端检索数据库中的用户数量以及每个用户的信息。[OperationContract] public int CountUsers() { return 2; } [OperationContract] public User GetUser(int id) { if (id == 1) { return new User() { IsMember = true, Name = "Paul", Age = 24}; } else { return new User() { IsMember = false, Name = "John", Age = 64}; } }
不要忘记将 OperationContractAttribute 属性应用于每项操作。您所定义的服务基于 Windows Communication Foundation (WCF) 技术。本主题仅使用 WCF 的基本功能。有关更多信息,请参阅 WCF 文档,具体为设计和实现服务这一主题。
-
服务中使用的类型需要遵守称为数据协定的特定规则。创建有效类型的最简单方法是确保它是公用的,具有公共成员,并且具有不需要任何参数的公共构造函数。全部公开即隐式定义了此类型的数据协定。例如,可以按如下方式实现
GetUser
操作中的User
类型(在 SilverlightApplication1.Web 命名空间中,CustomerService
类后面)。public class User { public bool IsMember { get; set; } public string Name { get; set; } public int Age { get; set; } }
默认情况下,DataContractSerializer 序列化所有公开可见的类型,包括这些类型的所有公共的读/写属性和字段。因此,
User
类的成员将是可序列化的。有关默认的序列化行为的信息,请参见可序列化类型。您不需要应用 DataContractAttribute 和 DataMemberAttribute 以选择使用序列化过程。有关服务中可以使用的类型的更多信息,请参见数据协定序列化程序支持的类型。
-
若要在 Visual Studio 中测试服务实现,请选择“解决方案资源管理器”中的 CustomerService.svc 文件,然后右击并选择“在浏览器中查看”(或按 Ctrl + F5)以显示服务的测试页。您应看到“CustomerService 服务”测试页,这样便可确定该服务可用。
创建服务时的其他注意事项
对 Silverlight 可以访问的服务类型存在一些限制。如果不使用“启用 Silverlight 的 WCF 服务”模板创建 WCF 服务,则必须确保使用 BasicHttpBinding 配置 WCF 服务,以便支持该服务。同样支持 PollingDuplexHttpBinding。有关使用双工绑定的更多信息,请参见生成和访问双工服务。请注意,不能使用其他 WCF 绑定,因为 Silverlight 客户端不支持这些绑定所需的功能。例如,WSHttpBinding 所需的会话和事务即不受支持。有关访问 SOAP 服务(包括并非使用 WCF 甚至是并非使用 .NET Framework 创建的 SOAP 服务)时的 Silverlight 4 限制的更多信息,请参见访问 SOAP 服务。
当在承载 Silverlight 应用程序的网站之外的任何位置创建服务时,都会产生跨域问题。在 Silverlight 应用程序和服务之间进行跨域调用时会出现安全漏洞,且必须由相应跨域策略明确启用该跨域调用。有关描述如何实现这类策略的过程,请参见使服务跨域边界可用。
当在 Visual Studio 中调试多个单独的 Web 应用程序项目时,常常会出现这种情况,因为已向每个 Web 应用程序分配了不同的端口号。例如,您的 Silverlight 应用程序可能位于 http://localhost:1111 上,而您的服务可能位于 http://localhost:2222 上。即使计算机名相同 (localhost),端口号的不同也足以造成跨域问题,您必须按照使服务跨域边界可用中的描述进行处理。
当您使用“启用 Silverlight 的 WCF 服务”模板时,Web.config 文件会自动改为启用 ASP.NET 兼容模式,以便于将您的支持服务的 Silverlight 应用程序与 ASP.NET 安全模型相集成。这样您就可以完成很多工作,例如,可以限制仅通过身份验证的用户拥有对特定服务的访问权限,或者限制拥有特定角色的用户拥有访问权限。出现在相同项目中的所有 WCF 服务都必须使用 AspNetCompatibilityRequirements AspNetCompatibilityRequirements 特性配置为允许 ASP.NET 兼容模式。可以为使用“启用了 Silverlight 的 WCF 服务”模板添加的所有服务自动执行此操作,但您必须为可能在同一项目中具有的所有其他 WCF 服务手动完成此操作。此操作的备选操作是关闭 Web.config 中的 ASP.NET 兼容性模式(如果您的服务都不需要该模式)。
当使用服务访问数据库时,通过 ADO.NET Entity Framework 使用从数据库自动生成的类型很方便。这些生成的类型完全与专门供 Silverlight 使用的服务兼容。还经常便于在服务实现中使用 LINQ。例如,假定您的数据库具有 Users 表,且已从您的数据库生成 MyDatabaseEntities
实体模式,则可以按照以下方式实现用于按名称查找用户的操作。
示例
以下示例总结了完成以上过程后 CustomerService.svc.cs 文件中的代码。
//CustomerService.svc.cs using System; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Activation; namespace SilverlightApplication1.Web { [ServiceContract(Namespace = "")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class CustomerService { [OperationContract] public int CountUsers() { return 2; } [OperationContract] public User GetUser(int id) { if (id == 1) { return new User() { IsMember = true, Name = "Paul", Age = 24 }; } else { return new User() { IsMember = false, Name = "John", Age = 64 }; } } } // All public read/write properties and fields of a type are serialized by default. public class User { public bool IsMember { get; set; } public string Name { get; set; } public int Age { get; set; } } }