数据点

处理 WCF 数据服务中的实体框架验证

Julie Lerman

下载代码示例

Julie Lerman
我写此列继微软生成会议。当然,所有的激动之情,在生成的核心是新地铁 UI 的 Windows 8 的新 Windows 运行库 (WinRT) 之上的。如果你的数据极客,你可能已查看过,看看有哪些选项提供"地铁风格"的应用程序数据。在此早期的预览中,您可以提供从文件存储或从 Web 数据。如果您想与关系数据进行交互,基于 Web 的选项将 XML 或 JSON 包括通过 HTTP、 套接字和服务。在服务方面,地铁样式应用程序将提供客户端库消费 OData,这意味着你有今天使用通过 Microsoft OData 任何经验。NET 框架,Silverlight 或其他客户端库会给你很大优势当你准备在地铁样式应用程序中使用 OData。

这一点,我会花这列使用 OData。实体框架 (EF) 释放包含代码第一次和 DbContext 介绍了一个新的验证 API。将介绍如何利用内置的服务器端验证时你 EF 代码第一种模式被公开为 OData 通过 WCF 数据服务。

验证 API 基础知识

您可能已经熟悉配置如所需的属性或 MaxLength 到使用数据的注释或流利 API 的类属性。新的验证 API 可以自动检查这些属性。"实体 · 框架 4.1 验证",在 MSDN 数据开发人员中心中的一篇文章 (msdn.microsoft.com/data/gg193959),说明了这一点,以及如何应用规则的 IValidatableObject 接口和 ValidateEntity 方法。虽然你可能已经被验证数据的注释和 IValidatable­对象在客户端,也可以将其规则签随您添加的任何 ValidateEntity 逻辑服务器端。或者,您也可以选择触发验证在服务器代码中的需求。

在这里,例如,是一个简单的人类,使用两个数据注释 (第一个指定姓氏属性是必需的和其他设置 IdentityCard 字符串字段的最大长度):

public class Person
{
  public int PersonId { get; set; }
  public string FirstName { get; set; }
  [Required]
  public string LastName { get; set; }
  [MaxLength(10)]
  public string IdentityCardNumber { get; set; }
}

默认情况下,EF 将调用 SaveChanges 时执行验证。 如果这些规则失败,EF 将引发 Sys­透射电镜。Da­ta。Ent­的碳黑。DbEntityValidationException — — 有一种有趣的结构。 每个验证错误描述的 DbValidationError,和 DbValidationErrors 按 EntityValidationErrors 组的对象实例。

例如, 图 1 显示如果 EF 检测到两个不同的人实例验证问题,将会引发的 DbEntityValidationException。 第一个 EntityValidationErrors 对象包含一组 DbValidationErrors 人的单一实例那里有两个错误: 没有姓氏和 IdentityCard 了太多的字符。 第二个人实例有一个单一的问题 ; 因此,在第二个 EntityValidationErrors 对象是只有一个 DbValidationError。

DbEntityValidationException Contains Grouped Sets of Errors
图 1 DbEntityValidationException 包含错误的分组的集

在 MSDN 数据开发人员中心文章中我提到,我显示异常被传递回模型-视图-­控制器 (MVC) 的应用程序知道如何发现和显示特定的错误。

在分布式应用程序,但错误可能不会它返回到客户端使用和报告这么容易。 顶级的异常可能会返回,而客户端应用程序可能都不知道如何深入查找错误 DbEntityValidationException。 许多应用程序,您可能甚至访问 System.Data.Entity 命名空间,因此没有 DbEntityValidationException 的知识。

另外一个问题是如何 WCF 数据服务传送在默认情况下的例外情况。 在客户端,你只会得到一条消息,告诉您"处理此请求时出错"。但这里的关键短语是""默认值。您可以自定义您的 WCF 数据服务,DbEntityValidationExceptions 的分析,并向客户端返回有用的错误的信息。 这是我将重点为此列的其余部分。

默认情况下的 WCF 服务结果数据隐藏验证错误

我的模型承载 DbContext 数据层中,我已经打电话给 PersonModelContext:

public class PersonModelContext : DbContext
  {
    public DbSet<Person> People { get; set; }
  }

我有一种简单的数据服务,公开人从用于读取和写入此上下文类型:

public class DataService : DataService<PersonModelContext>
{
  public static void InitializeService(DataServiceConfiguration config)
  {
    config.SetEntitySetAccessRule("People", EntitySetRights.All);
    config.DataServiceBehavior.MaxProtocolVersion =
      DataServiceProtocolVersion.V3;
  }
}

因为我用代码第一次,我将不得不做得到 WCF 数据服务来进行一些调整。 调整,而不是我已经取代微软。NET 框架 4 System.Data.Services 和 System.Data.ClientServices 的 Microsoft.Data.Services 和 Microsoft.Data.ClientServices 的库,从 2011 年 3 月与 WCF 数据服务 CTP (请参阅 bit.ly/mTI69m),有建于这些调整。 这就是为什么 DataServiceProtocolVersion 设置为 V3。

最后,我消费有一个简单的控制台应用程序,使用下列方法来插入一个人的服务:

private static void InsertPersonNoLastName()
{
  var person = new Person
  {
    FirstName = "Julie",
    IdentityCardNumber="123456789",
  };
  var context = new PersonModelContext
   (new Uri("http://localhost:43447/DataService.svc"));
 context.AddToPeople(person);
 context.SaveChanges();
}

请注意,我已经忘了姓氏属性设置。 因为姓氏被配置为要求,EF 将引发 excep­解释数据服务,但这个控制台应用程序将只收到消息 DataServiceRequestException 描述早期 ("时出错处理此请求。")。 如果你深入的内部异常,你会发现它包含相同的消息和任何其他详细信息。

WCF 数据服务确实有的设置,以便让您寄回的异常消息的更多详细信息的 InitializeService 方法添加以下内容:

#if DEBUG
  config.UseVerboseErrors = true;
#endif

现在 (载于该服务的 XML 响应) 的内部消息,告诉您: "验证失败的一个或多个实体。 看到 'EntityValidationErrors' 属性的更多详细信息"。但不幸的是,EntityValidationErrors 不做传递回与异常。 因此,你知道验证 API 发现一个或多个问题,但你不能发现更多关于该错误。 请注意我裹 UseVerboseErrors 编译器指令。 UseVerboseErrors 应仅用于调试 — — 你不想在生产代码中。

重写 HandleException 方法

WCF 数据服务公开了一种称为 HandleException 的虚拟 (Overrideable) 方法。 这允许您捕获任何服务中发生的异常、 分析和构建您自己的 DataServiceException 返回到调用方。 正是在这种方法可以分析出任何验证错误,将更有意义的信息返回给调用应用程序。 方法的签名是:

protected override void HandleException(HandleExceptionArgs args)

HandleExceptionArgs 类型所具有的许多属性: 异常,ResponseContentType,esponseStatusCode,响应­读写、 UseVerboseErrors

我感兴趣的是异常的属性。 这您可以在其中捕获并确定异常引发由验证 API — — DbEntityValidationException。 您还可以处理任何其他类型的错误,在这里,但我会集中讨论寻找和解析验证异常。 我得在我使用的 System.Data.Entity.Validation 命名空间顶部的类声明,我坚决不去类型的异常。

出发只是一个单一的实体被进行了验证,这就是为什么我只查询的第一个实体的推定与­载异常,如中所示的 ValidationErrors 图 2。 如果您想要验证多个对象的服务,请务必使用 SaveChangesOptions.Batch 参数,当您调用 SaveChanges。 否则为只会保存一个对象和一次验证,并且一旦你打一个错误,将保存或验证没有更多的对象。

构建更有用的异常消息图 2

protected override void HandleException(HandleExceptionArgs args)
{
  if (args.Exception.GetType()==
    typeof(DbEntityValidationException))
  {
    var ex=args.Exception as DbEntityValidationException;
    var errors = ex.EntityValidationErrors.First().ValidationErrors.ToList();
    var errorMessage=new StringBuilder();
    foreach (System.Data.Entity.Validation.DbValidationError e in errors)
    {
      errorMessage.AppendLine(e.ErrorMessage);
    }
    args.Exception = new DataServiceException(500, errorMessage.ToString());
  }
}

在此方法中发生了什么,是我第一次检查,看看是否异常引发由验证 API 的类型。 如果是,我拉的异常到变量"ex"。接下来,我查询所有的 DbValidationErrors EntityValidationErrors 在例外中的第一组所载的列表。 然后建立一个新的错误字符串,使用每个 EntityValidationError 的错误信息属性,并将该字符串传递回调用应用程序的一种新型数据­ServiceException。 EntityValidationError 其他属性,但它建立了完整的错误消息,进到代码使用的名称属性和验证问题。 代码第一,您可以指定自定义错误消息,但为此演示的目的,我很高兴与默认值。 在此示例中,该消息是"姓氏字段是必需的"。请注意 DataServiceException 的构造函数的重载的数。 我把它简单,只"内部服务器错误"500 代码和一个字符串提供我想要传达的消息。

解析客户端上的新的异常

现在,在客户端,你仍然会异常,说:"在处理此请求,时发生错误",但这一次的内部异常包含的消息"姓氏字段是必需"。

但它不是一个简单的字符串。 DataServiceRequestException 的消息格式的 HTTP Response 中通过 HTTP 提出要求,是因为:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns=
  "https://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code></code>
  <message xml:lang="en-US">The LastName field is required.&#xD;
  </message>
</error>

我建造服务中的 DataServiceException 的重载,这些重载的其中一个允许您插入自定义的错误代码。 我用的如果自定义代码将显示在 <code> 错误的元素。 如果您正在从一个 Web 应用程序中调用服务,您可以直接在您的 UI 中显示的 HTTP 响应。 否则,您可能需要解析它,以便您能够处理以何种方式在您的应用程序中使用错误处理的异常。

我用 LINQ to XML 提取邮件,然后我可以在我的控制台应用程序中显示它。 我叫 SaveChanges 在 try/catch 块,分析并显示错误消息 (请参见图 3)。 图 4 显示客户端的异常的结果。

图 3 解析和显示从服务返回的错误消息

try
{
  context.SaveChanges();
}
catch (Exception ex)
{
  var sr = new StringReader(ex.InnerException.Message);
  XElement root = XElement.Load(sr);
  IEnumerable<XElement> message =
    from el in root.Elements()
    where el.Name.LocalName == "message"
    select el;
  foreach (XElement el in message)
    Console.WriteLine(el.Value);
  Console.ReadKey();
}

Parsed Error Message Displayed in the Client
图 4 解析客户端中显示的错误消息

现在我就把另一个扳手扔到 InsertPerson 方法。 除了要忽略的姓氏属性,我会把字符太多放进身份­卡属性。 请记住此属性被配置为拥有 MaxLength 的 10:

var person = new Person
{
  FirstName = "Julie",
  IdentityCardNumber="123456789ABCDE"
};

现在的 HandleException 方法将查找两个 DataValidation­人实例的服务试图更新错误。 StringBuilder 将包含两行消息 — — 一个描述的姓氏属性与另一种解释的问题的 IdentityCard 属性的问题。

在控制台应用程序中,这将被视为单个邮件中异常:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns=
  "https://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code></code>
  <message xml:lang="en-US">The field IdentityCardNumber must be a string or array type with a maximum length of '10'.&#xD;
    The LastName field is required.&#xD;
  </message>
</error>

如中所示,LINQ to XML 分析器将接中继到控制台,消息图 5

Console App Displaying Multiple Errors for a Single Entity
图 5 控制台应用程序显示为一个单一的实体的多个错误

验证连接断开时,甚至受益

您已经看到如何,使用一组简单的使用代码第一数据注释应用的要求,您可以捕获和分析 EF 验证异常、 他们返回到客户端和,在客户端,解析通过 HTTP 返回的异常。 无论您正在使用的属性配置或更复杂的规则,您可以指定与 IValidationObject 或通过重写 ValidateEntity 方法通过应用的有效性规则,EF 将始终返回 DbEntity­ValidationExceptions。 你现在知道如何通过这些分析,并且可以扩展以容纳多个对象,提供错误消息包含更多的详细信息,并处理它们所要求的应用程序客户端或服务器上的逻辑。

因为 WCF 数据服务返回 OData,你可以使用这些服务,并利用实践与今天的验证,以便您可以准备做未来地铁式技术同。

Julie Lerman 是 Microsoft MVP。净的导师和顾问住在山上的佛蒙特。您可以找到她提交数据访问和其他的微软。用户组和世界各地的会议的净主题。在她的博客 thedatafarm.com/blog 是备受称赞的书,"编程实体框架"(O'Reilly 介质,2010年) 的作者。跟她在 Twitter 上 twitter.com/julielerman

多亏了以下技术的专家,检讨这篇文章: Mike Flasko