使用 Entity Framework 实现 WCF 服务
使用 Entity Framework 作为后端 ORM 实现一个简单的 WCF 服务。
更新
本文的 .NET 4.5 更新版本可在以下链接找到:使用 Entity Framework 实现 WCF 服务 (v4.5)。
引言
这是我撰写的关于 WCF/LINQ 的另一篇文章。在 CodeProject.com 上的前三篇文章中,我将解释 Windows Communication Foundation (WCF) 的基础知识,包括
在其他文章中,我将解释 LINQ、LINQ to SQL、Entity Framework 和 LINQ to Entities。以下是我为 LINQ、LINQ to SQL 和 LINQ to Entities 撰写的文章
概述
在上一篇文章 (实现一个基本的 Hello World WCF 服务) 中,我们学习了如何从头手动创建一个基本的 WCF 服务。在那篇文章中,我们还手动创建了一个宿主应用程序,生成了代理和配置文件,并创建了一个测试客户端来测试 WCF 服务。您可以回顾该文章以了解 WCF 服务的内部工作原理。
现在在本文中,我们将使用内置的 Visual Studio WCF 模板实现一个简单的 WCF 服务。这个简单的 WCF 服务将只有一个操作,`GetProduct`。此操作将接受一个整数输入作为产品 ID,并连接到后端数据库,使用 LINQ to Entities 检索指定产品 ID 的产品详细信息。Microsoft 示例数据库 Northwind 将用作后端数据库,Visual Studio 2010 将用作 IDE。
在未来的文章 (使用 Entity Framework 实现 WCF 服务的并发控制) 中,我们将实现另一个操作 `UpdateProduct`,以通过 Entity Framework 更新数据库中的产品。我们将在该文章中介绍并发控制,并将在该文章中创建一个 WPF 测试客户端来测试支持并发的 `UpdateProduct` 操作。
在本文中,我们将按以下顺序构建解决方案
- 创建 WCF 服务
- 使用 WCF 模板创建新解决方案和项目
- 创建服务接口
- 创建操作契约
- 创建数据契约
- 实现服务接口
- 修改 `app.config` 文件
- 使用 WCF 测试客户端测试服务
- 将 LINQ to Entities 应用于 WCF 服务
- 准备数据库
- 建模 Northwind 数据库
- 重命名 EF Product 类
- 使用 EF 从数据库检索产品详细信息
- 将 ProductEntity 对象转换为 Product 对象
- 使用 EF 测试 WCF 服务
- 测试异常
创建 WCF 服务
在本文的前半部分,我们将创建一个简单的 WCF 服务。这个 WCF 服务将包含 `GetProduct` 操作,它将向客户端返回一个产品对象。此 WCF 服务中的产品详细信息是硬编码的,但 ID 将来自客户端输入。在本文的稍后部分,我们将增强此 WCF 服务,以使用 LINQ to Entities 从真实的后端数据库检索真实的产品详细信息。
使用 WCF 模板创建新解决方案和项目
我们需要为本文创建一个新解决方案,并向此解决方案添加一个新 WCF 项目。Visual Studio 2010 中有一些内置的 WCF 服务模板。在本文中,我们将使用服务库模板来创建 WCF 服务。
按照以下步骤使用服务库模板创建 WCF 和 EF 解决方案和项目
- 启动 Visual Studio 2010,选择菜单选项“文件 | 新建 | 项目...”,您将看到“新建项目”对话框。
- 在“新建项目”窗口中,指定“Visual C# | WCF | WCF 服务库”作为项目模板,“WCFandEFService”作为(项目)名称,“WCFandEFSolution”作为解决方案名称。确保选中“为解决方案创建目录”复选框。
- 单击“确定”按钮,解决方案将随附一个 WCF 项目一起创建。该项目已经有一个 `IService1.cs` 文件来定义服务接口,以及 `Service1.cs` 来实现服务。它还有一个 `app.config` 文件,我们将在稍后介绍。
现在我们已经使用 C# WCF 服务库模板创建了 WCF 服务项目。该项目实际上是一个应用程序,包含一个示例 WCF 服务、一个宿主应用程序 (WcfSvcHost) 和一个 WCF 测试客户端。这意味着现在我们可以使用内置的 WCF 测试客户端来调用示例 WCF 服务。
要尝试,只需按 Ctrl+F5,您将看到 WCF 测试客户端已启动并运行!双击左侧面板中的 `GetData` 操作,在右侧面板的“值”文本框中输入一个数字,然后单击“调用”按钮,您将看到 WCF 服务已被调用:WCF 服务的响应现在显示在右侧面板的底部。
注意:为了运行 WCF 测试客户端,您必须以本地管理员身份登录到您的机器。您可能还需要以管理员身份运行 Visual Studio。我们将在稍后讨论更多关于 WCF 测试客户端的内容。
创建服务接口
在上一节中,我们使用 WCF 服务库模板创建了一个 WCF 项目。在本节中,我们将创建服务接口契约。
由于已经为我们创建了两个示例文件,我们将尽量重复使用它们。然后,我们将开始自定义这两个文件以创建服务契约。
创建操作契约
要创建服务操作契约,我们需要打开 `IService1.cs` 文件并执行以下操作
- 将接口名称从 `IService1` 更改为 `IProductService`。如果您在接口定义行之前看到警告消息,请不要担心,我们将在以下步骤之一中更改 `web.config` 文件。
- 将第一个操作契约定义从以下行更改
string GetData(int value);
改为以下行
Product GetProduct(int id);
- 将文件名称从 `IService1.cs` 更改为 `IProductService.cs`。
- 删除操作 GetDataUsingDataContract
通过这些更改,我们定义了一个服务契约。它用于获取特定产品 ID 的产品详细信息。用于定义服务契约的产品类型尚未定义。我们将在本节之后立即定义它。
现在 `IProductService` 的服务接口内容应如下所示
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCFandEFService
{
[ServiceContract]
public interface IProductService
{
[OperationContract]
Product GetProduct(int id);
}
// original code for CompositeType
}
注意:这不是 `IProductService.cs` 文件的全部内容。此文件的底部现在仍应具有 `CompositeType` 类,我们将在下一节中将其更改为我们的 `Product` 类型。
创建数据契约
SOA 设计的另一个重要方面是,您不应假设消费应用程序支持复杂的对象模型。服务边界定义的一部分是用于作为操作参数或返回值传递的复杂类型的数据契约定义。
为了实现最大的互操作性和与 SOA 原则的一致性,您不应在服务边界之间传递任何 .NET 特定类型,例如 `DataSet` 或 `Exception`。您应该坚持使用相当简单的数据结构对象,例如具有属性和支持成员字段的类。您可以传递具有嵌套复杂类型的对象,例如“具有订单集合的客户”。但是,您不应假定消费者能够支持面向对象构造,例如继承或可互操作 Web 服务的基类。
在我们的示例中,我们将创建一个复杂数据类型来表示产品对象。此数据契约将具有五个属性:`ProductID`、`ProductName`、`QuantityPerUnit`、`UnitPrice` 和 `Discontinued`。这些将用于与客户端应用程序通信。例如,供应商可以调用 Web 服务来更新特定产品的价格,或将产品标记为停产。
最好将数据契约放在单独程序集中的单独文件中,但为了简化我们的示例,我们将把 `DataContract` 放在与服务契约相同的文件中。因此,我们将如下修改文件 `IProductService.cs`
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public int ProductID { get; set; }
- 将 `DataContract` 名称从 `CompositeType` 更改为 `Product`。
- 删除以下两行代码
- 删除旧的 `BoolValue` 和 `StringValue` `DataMember` 属性。然后,对于每个产品属性,添加一个 `DataMember` 属性。例如,对于 `ProductID`,我们将具有此 `DataMember` 属性
完成的服务契约文件 `IProductService.cs` 的数据契约部分现在应如下所示
[DataContract]
public class Product
{
[DataMember]
public int ProductID { get; set; }
[DataMember]
public string ProductName { get; set; }
[DataMember]
public string QuantityPerUnit { get; set; }
[DataMember]
public decimal UnitPrice { get; set; }
[DataMember]
public bool Discontinued { get; set; }
}
现在文件 `IProductService.cs` 的最终内容应如下所示
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCFandEFService
{
[ServiceContract]
public interface IProductService
{
[OperationContract]
Product GetProduct(int id);
}
[DataContract]
public class Product
{
[DataMember]
public int ProductID { get; set; }
[DataMember]
public string ProductName { get; set; }
[DataMember]
public string QuantityPerUnit { get; set; }
[DataMember]
public decimal UnitPrice { get; set; }
[DataMember]
public bool Discontinued { get; set; }
}
}
实现服务接口
要实现我们在上一节中定义的服务接口,请打开 `Service1.cs` 文件并执行以下操作
public class ProductService : IProductService
public Product GetProduct(int id)
{
// TODO: retrieve the real product info from DB using EF
Product product = new Product();
product.ProductID = id;
product.ProductName = "fake product name";
product.UnitPrice = (decimal)10.0;
return product;
}
在此方法中,我们创建了一个假产品并将其返回给客户端。稍后,我们将从此方法中删除硬编码产品,并使用 EF 从数据库检索真实的产品信息。
文件 `ProductService.cs` 的内容应如下所示
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCFandEFService
{
public class ProductService : IProductService
{
public Product GetProduct(int id)
{
// TODO: retrieve the real product info from DB using EF
Product product = new Product();
product.ProductID = id;
product.ProductName = "fake product name";
product.UnitPrice = (decimal)10.0;
return product;
}
}
}
- 将类名从 `Service1` 更改为 `ProductService`。使其继承自 `IProductService` 接口,而不是 `IService1`。类定义行应如下所示
- 删除 `GetData` 和 `GetDataUsingDataContract` 方法。
- 添加以下方法,以获取产品
- 将文件名称从 `Service1.cs` 更改为 `ProductService.cs`。
修改 `app.config` 文件
由于我们已更改服务名称,因此我们必须对配置文件进行相应的更改。注意:当您重命名服务时,如果您使用了 Visual Studio 的重构功能,则 Visual Studio 可能已经完成了一些以下任务。
请按照以下步骤更改配置文件
- 从解决方案资源管理器中打开 `app.config` 文件。
- 将所有 `Service1` 实例更改为 `ProductService`。
现在 `app.config` 文件的内容应如下所示
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<service name="WCFandEFService.ProductService">
<host>
<baseAddresses>
<add baseAddress = "https://:8732/
Design_Time_Addresses/WCFandEFService/ProductService/" />
</baseAddresses>
</host>
<endpoint address ="" binding="wsHttpBinding"
contract = "WCFandEFService.IProductService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding"
contract = "IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
使用 WCF 测试客户端测试服务
由于在此示例中我们使用的是 WCF 服务库模板,因此我们现在可以测试此 Web 服务。正如我们创建此项目时指出的那样,此服务将托管在 Visual Studio 2010 WCF 服务宿主环境中。
要启动服务,请按 F5 或 Ctrl+F5。WcfSvcHost 将启动,WCF 测试客户端也将启动。这是一个 Visual Studio 2010 内置的 WCF 服务库项目测试客户端。
注意:为了运行 WCF 测试客户端,您必须以本地管理员身份登录到您的机器。如果您已将端口从默认值 8732 更改为其他端口(例如 8080),您还必须以管理员身份启动 Visual Studio。否则,您可能会收到“访问被拒绝”错误。
现在,通过此 WCF 测试客户端,我们可以测试我们的简单 `GetProduct` 操作。
- 在客户端的左侧面板中,双击 `GetProduct` 操作;`GetProduct` 请求将显示在右侧面板上。
- 在此请求面板中,为产品 ID 指定一个整数,然后单击“调用”按钮,让客户端调用服务。您可能会收到一个对话框,警告您通过网络发送信息的安全性。单击“确定”按钮以确认此警告(您可以选中“以后不再显示此消息”选项,这样它就不会再次显示)。
现在,状态栏中将显示“正在调用服务...”消息,因为客户端正在尝试连接到服务器。由于需要在后台执行多项操作,此初始连接可能需要一段时间。一旦建立连接,将创建一个通道,并且客户端将调用服务以执行请求的操作。一旦服务器端操作完成,响应包将发送回客户端,并且 WCF 测试客户端将在右下角面板中显示此响应。
如果您在调试模式下启动了测试客户端(通过按 F5),您可以在 `ProductService.cs` 文件中的 `GetProduct` 方法内的某行设置断点,当单击“调用”按钮时,将命中该断点,以便您可以像调试任何其他 .NET 应用程序一样调试服务。
请注意,无论您使用哪个产品 ID 来检索产品(产品 ID 字段除外),响应始终相同。具体来说,产品名称是硬编码的,如图所示。此外,从客户端响应面板中,我们可以看到 `Product` 对象的几个属性已分配了默认值。
另外,由于产品 ID 是 WCF 测试客户端的整数值,因此您只能输入整数。如果输入非整数值,当您单击“调用”按钮时,将收到一个错误消息框,警告您输入的值类型错误。
请求/响应包默认以网格显示,但您可以选择以 XML 格式显示它们。只需从右侧面板底部选择“XML”选项卡,您将看到 XML 格式的请求/响应包。从这些 XML 字符串中,您将发现它们是 SOAP 消息。
除了测试操作外,您还可以查看 Web 服务的配置设置。只需双击左侧面板中的“配置文件”,配置文件将显示在右侧面板中。这将向您显示服务的绑定、服务地址和服务契约。
注意:您在此处看到的配置文件与实际配置文件并不完全相同。它隐藏了一些信息,例如调试模式和服务行为,并包含有关可靠会话和压缩模式的一些附加信息。
如果您对测试结果满意,只需关闭 WCF 测试客户端,您将返回到 Visual Studio IDE。请注意,一旦您关闭客户端,WCF 服务主机就会停止。这与在 ASP.NET 开发服务器中托管服务不同,在 ASP.NET 开发服务器中,关闭客户端后,ASP.NET 开发服务器仍保持活动状态。
将 LINQ to Entities 应用于 WCF 服务
在前面几节中,我们创建了一个简单的 WCF 服务,用于获取输入产品 ID 的生产详细信息。生产详细信息在服务实现中是硬编码的。
在以下几节中,我们将把 LINQ to Entities 应用于 WCF 服务。我们将通过 Entity Framework 连接到数据库,并从数据库中检索真实的产品信息。
准备数据库
在本文中,我们将使用 Microsoft 示例数据库 Northwind 作为后端数据库。此数据库在 SQL Server 2005 或 SQL Server 2008 中默认未安装,因此首先,我们需要将其安装到我们的数据库服务器。
- 下载数据库包。只需在 Internet 上搜索“Northwind 示例数据库下载”,或访问此页面:http://www.microsoft.com/downloads/details.aspx?FamilyId=06616212-0356-46A0-8DA2-EEBC53A68034&displaylang=en 并下载文件 `SQL2000SampleDb.msi`。注意:此示例数据库专为 SQL Server 2000 设计,但也可用于 SQL Server 2005 和 SQL Server 2008。
- 将其安装(解压)到:`C:\SQL Server 2000 Sample Databases`。
- 将 `Northwnd.mdf` 和 `Northwnd.ldf` 的安全权限更改为您的 SQL Server 服务帐户用户可读/写(或者只授予 Everyone 完全访问权限)。
- 打开 SQL Server 2005/2008 Management Studio。
- 连接到您的数据库引擎。
- 右键单击“数据库”节点,然后从上下文菜单中选择“附加...”,如下图所示的 SQL Server Management Studio 图示
- 在弹出的“附加数据库”对话框中,单击“添加”,浏览到文件 `C:\SQL Server 2000 Sample Databases\NORTHWND.MDF`,单击“确定”,您现在已将 Northwind 数据库附加到您的 SQL Server 2005 或 2008 引擎。
建模 Northwind 数据库
现在我们已经准备好数据库,我们可以使用 Entity Framework 对此数据库进行建模。在对数据库进行建模之后,我们将使用 LINQ to Entities 从数据库中检索真实的产品信息。
您可以按照以下步骤将实体数据模型添加到项目中。
这将生成一个名为 `Northwind.designer.cs` 的文件,其中包含 Northwind 数据库的对象上下文。此文件还包含 `Product` 实体类。
- 在“解决方案资源管理器”中,右键单击项目项“WCFandEFService”,选择菜单选项“添加 | 新建项...”,然后选择“Visual C# 项 | ADO.NET 实体数据模型”作为模板,并输入 `Northwind.edmx` 作为名称。
- 单击“添加”按钮将带您进入实体数据模型向导。在此向导中,选择“从数据库生成”,单击“下一步”按钮,将弹出连接对话框窗口。输入您的数据库服务器名称,例如 localhost,指定登录详细信息,并选择 Northwind 作为数据库,然后单击“确定”按钮关闭此窗口。
- 在实体数据模型向导中,单击“下一步”按钮进入“选择您的数据库对象”屏幕。选择“Products”表,然后单击“完成”按钮关闭向导。
重命名 EF Product 类
如果您现在编译项目,您将看到错误“类型‘WCFandEFService.Product’的声明缺少部分修饰符;此类型的另一个部分声明已存在”。这是因为在本文前面,我们为服务数据契约创建了一个 `Product` 类,但现在 Entity Framework 又创建了另一个 `Product` 类。两者都在同一个命名空间中,一个是部分类,而另一个不是。
有两种方法可以解决此问题。首先,我们可以删除我们自己的 `Product` 类定义,并将 EF `Product` 类公开为 WCF 服务数据契约类。这样,我们可以在整个项目中只使用一个 `Product` 实体类,并避免一些实体定义的重复。但是,将 EF 实体类公开到 WCF 服务之外不是最佳实践。您可以在 Google 上搜索“将 EF 实体公开为数据契约”以了解人们对此的看法。
另一种方法是分离我们自己的 `Product` 类和 EF `Product` 类。我们自己的 `Product` 类将作为 WCF 数据契约类,而 EF `Product` 类将作为 ORM 数据实体类。现在我们有了清晰的关注点分离,外部世界永远不会知道我们在底层使用了哪种 ORM。我喜欢这个想法,因此在本文中,我们将采用这种方法。
但是,将我们自己的 `Product` 类和 EF `Product` 类保留在同一个项目中会导致问题,正如我们在本节开头所指出的。为了解决这个问题,我们有几个选择。首先,我们可以向 WCF 服务解决方案添加数据访问层,并将 EF 类放在数据访问层中。实际上,这是开发企业级 WCF 服务的首选方法,正如我在我的 WCF 和 LINQ to Entities 书中讨论的那样(您可以在本文末尾找到有关此书的更多信息)。但在本文中,为了保持简单易懂,我们将只为 WCF 服务提供一个层,因此我们不会采用这种方法。第二种方法是将 EF 实体类放在单独的命名空间中,这样两个 `Product` 类就可以毫无问题地共存。但是,在同一个项目中拥有两个 `Product` 类会令人困惑,即使它们位于两个不同的命名空间中。因此,我们将采用第三种方法,即,将 EF `Product` 实体类重命名为 `ProductEntity`。
要重命名 EF `Product` 类,请打开 `Northwind.edmx` 文件,单击类名,然后重命名它。重命名后的模型应如下所示
重命名 EF `Product` 类后,如果您重新生成服务,您应该不会看到任何错误。
使用 EF 从数据库检索产品详细信息
现在,在 `GetProduct` 方法中,我们可以使用以下语句通过 LINQ to Entities 从数据库中获取产品详细信息
NorthwindEntities context = new NorthwindEntities();
var productEntity = (from p
in context.ProductEntities
where p.ProductID == id
select p).FirstOrDefault();
这里,我们首先为 EF 模型创建了一个 `ObjectContext` 对象,然后我们使用 LINQ to Entities 语句从数据库中获取给定 ID 的产品实体。由于 LINQ to Entities 语句的返回结果是 `IQueryable`,我们调用了 `FirstOrDefault` 方法只获取第一条记录。如果查询没有从数据库中产生任何结果,此方法也会返回 null 结果。
将 ProductEntity 对象转换为 Product 对象
但是,我们不能将此产品对象返回给调用者,因为此产品是 `ProductEntity` 类型,这不是调用者期望的类型。调用者期望的返回值是 `Product` 类型,这是在服务接口中定义的数据契约。我们需要将此 `ProductEntity` 对象转换为 `Product` 对象。为此,我们将以下新方法添加到 `ProductService` 类
private Product TranslateProductEntityToProduct(
ProductEntity productEntity)
{
Product product = new Product();
product.ProductID = productEntity.ProductID;
product.ProductName = productEntity.ProductName;
product.QuantityPerUnit = productEntity.QuantityPerUnit;
product.UnitPrice = (decimal)productEntity.UnitPrice;
product.Discontinued = productEntity.Discontinued;
return product;
}
在这个转换方法中,我们将 `ProductEntity` 对象的所有属性复制到服务契约数据对象中,但最后三个属性除外——`UnitsInStock`、`UnitsOnOrder` 和 `ReorderLevel`。我们假设这三个属性仅在服务实现内部使用。外部调用者根本无法看到它们。
现在 `GetProduct` 方法应如下所示
public Product GetProduct(int id)
{
NorthwindEntities context = new NorthwindEntities();
var productEntity = (from p
in context.ProductEntities
where p.ProductID == id
select p).FirstOrDefault();
if (productEntity != null)
return TranslateProductEntityToProduct(productEntity);
else
throw new Exception("Invalid product id");
}
注意:在 `GetProduct` 方法内部,我们使用 EF 从数据库中检索产品详细信息后,我们首先测试对象是否为 null
。如果是,我们知道输入的 ID 在我们的数据库中不是有效的产品 ID。然后我们抛出一个异常来告诉客户端。但是,在真实的 WCF 服务中,您不应该向客户端抛出任何异常;相反,您应该定义并向客户端抛出故障。同样,有关故障的更多信息可以在我的 WCF 和 LINQ to Entities 书中找到。
文件 `ProductService.cs` 的最终内容应如下所示
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCFandEFService
{
public class ProductService : IProductService
{
public Product GetProduct(int id)
{
NorthwindEntities context = new NorthwindEntities();
var productEntity = (from p
in context.ProductEntities
where p.ProductID == id
select p).FirstOrDefault();
if (productEntity != null)
return TranslateProductEntityToProduct(productEntity);
else
throw new Exception("Invalid product id");
}
private Product TranslateProductEntityToProduct(
ProductEntity productEntity)
{
Product product = new Product();
product.ProductID = productEntity.ProductID;
product.ProductName = productEntity.ProductName;
product.QuantityPerUnit = productEntity.QuantityPerUnit;
product.UnitPrice = (decimal)productEntity.UnitPrice;
product.Discontinued = productEntity.Discontinued;
return product;
}
}
}
使用 EF 测试 WCF 服务
我们现在可以编译并测试支持 EF 的新服务。我们将仍然使用 WCF 测试客户端来简化该过程。
- 通过按 F5 或 Ctrl+F5 启动 WCF 服务主机应用程序和 WCF 服务测试客户端。
- 在 WCF 服务测试客户端中,双击 `GetProduct` 操作,以调出 GetProduct 测试屏幕。
- 在 ID 字段中输入值 56,然后单击“调用”按钮。
您将看到这次产品是从真实数据库返回的,而不是硬编码的。另外,请注意 `UnitsOnOrder` 属性未显示,因为它不是服务契约数据类型的一部分。
测试异常
现在,输入一个无效的产品 ID,例如 0,您将收到此错误消息
这是因为在 WCF 服务内部,我们无法在 Northwind 数据库中找到 ID 为 0 的产品,所以我们为此抛出了一个异常。由于我们没有在 `app.config` 中打开 `includeExceptionDetailInFaults` 标志,客户端应用程序无法获取异常详细信息。
要显示异常详细信息,您可以打开 `app.config` 文件,将 `includeExceptionDetailInFaults` 的值从 False 更改为 True,然后重试。这次,您将收到此错误消息
这一次,异常详细信息返回到客户端应用程序,如您所见;但是,不建议这样做。原因是不是所有客户端都能理解 .NET 异常。某些类型的客户端会将其视为未知故障。为了使其被所有类型的客户端理解,当无法从数据库中获取给定 ID 的产品时,我们需要定义并抛出故障。
抛出 .NET 异常而不是故障的另一个缺点是,一旦抛出异常,通信通道就不再有效(通道处于故障状态)。它不能用于后续的服务调用。
要测试此功能,首先输入一个无效的产品 ID,例如 0,以触发异常,然后输入另一个有效的产品 ID,例如 56,您将收到类似以下的错误消息
从错误消息中,我们知道通信通道现在已出现故障。您必须重新启动 WCF 测试客户端才能创建新的通信通道。或者,您可以通过选中“启动新代理”来强制测试客户端对服务调用使用新代理,但这在生产环境中不建议这样做,原因是为了性能。
摘要
在本文中,我们创建了一个简单的 WCF 服务,它使用 Entity Framework 从示例数据库中检索产品详细信息。本文的要点包括
- WCF 服务应具有明确的边界
- WCF 服务库模板可用于创建将由 WCF 服务主机托管的 WCF 服务,并且可以使用 WCF 服务测试客户端对其进行测试
- Entity Framework 可用于对后端数据库进行建模
- Entity Framework 实体表示服务的内部数据,不应将其暴露给客户端
- LINQ to Entities 可用于使用 EF 查询数据库
- 当存在异常时应使用故障契约
注意:本文基于我的著作《WCF 4.0 多层服务开发与 LINQ to Entities》(ISBN 1849681147)的第 4 章和第 9 章。本书是一本实践指南,教您如何在 Microsoft 平台上使用 WCF 和 LINQ to Entities 构建 SOA 应用程序。它已根据我之前的著作《WCF 多层服务开发与 LINQ》更新为 VS2010 版本。
通过本书,您可以通过完成实际示例并将其应用于您的实际任务来学习掌握 WCF 和 LINQ to Entities 概念。这是第一本也是唯一一本将 WCF 和 LINQ to Entities 结合在一个多层真实世界 WCF 服务中的书籍。它非常适合希望学习如何构建可扩展、强大、易于维护的 WCF 服务的初学者。本书内容丰富,包含示例代码、清晰的解释、有趣的示例和实用建议。对于 C++ 和 C# 开发人员来说,它是一本真正的实践性书籍。
您不需要具备 WCF 或 LINQ to Entities 的任何经验即可阅读本书。详细的说明和精确的屏幕截图将引导您完成探索 WCF 和 LINQ to Entities 新世界的整个过程。本书与其他 WCF 和 LINQ to Entities 书籍的区别在于,本书侧重于如何做,而不是为什么这样做,因此您不会被 WCF 和 LINQ to Entities 的大量信息所淹没。读完本书后,您会很自豪自己以最直接的方式使用了 WCF 和 LINQ to Entities。
您可以从亚马逊或出版商网站购买此书:https://www.packtpub.com/wcf-4-0-multi-tier-services-development-with-linq-to-entities/book。
您还可以在出版商网站上阅读本书的示例章节:http://www.packtpub.com/article/implementing-wcf-service-real-world。
更新
我的书的最新版本(适用于 Visual Studio 2013 / Windows 7 和 8.1)刚刚出版。您可以直接从出版商的网站上获取,地址是
或从亚马逊购买,地址如下
http://www.amazon.com/Multi-Layer-Services-Development-Entity-Framework/dp/1784391042