数据

本节说明 WCF RIA Services 如何处理涉及数据建模、检查数据有效性和确保数据并发的方案。当您提供一个接口用于从丰富 Internet 应用程序 (RIA) 的客户端更新、删除或创建数据时,在提交数据更改前,需要频繁对一些复杂的数据关系建模并确保用户的数据有效以及获取数据源的当前数据。

通常使用实体数据模型或 LINQ to SQL 对关系数据库中的数据进行建模。但是,您不需要在 RIA Services 项目中使用数据库。您可以使用任何对象类型来存储数据。客户端项目中便于数据操作的代码在某种意义上是不可知的数据源,即它实际无法识别数据访问技术或中间层使用的架构。

数据关系

RIA Services 提供一些功能,通过这些功能您可以与复杂的数据关系交互,如层次结构模型、多态继承模型和从多个实体合并值的表示模型以及包含多个域服务的值的模型。层次结构模型表示一个父子复合关系(如 OrderOrderDetails)或递归关系(如一个 Employee 模型,它包含指向另一个 Employee 模型中实体的 ManagerID 的字段)。有关更多信息,请参见复合层次结构

在继承模型中,您可以表示这样的数据结构:它包含 Customer 实体和派生自它的两个实体 PublicSectorCustomerPrivateSectorCustomer。使用域操作,您可以查询和更新这些类型。有关更多信息,请参见数据模型中的继承

在 RIA Services 版本 1.0 SP1 中新增了对非实体复杂类型的支持。特别针对代码生成、元数据、深度验证、更改跟踪、编辑会话和复杂类型参数提供了支持。这意味着自定义类型(如 Address)现在可作为传递到 DomainService 方法的实体属性或参数或者作为该方法的返回值。有关更多信息,请参见复杂类型主题。

在 RIA Services 版本 1.0 SP1 中新增了对共享具有多个域服务的实体的支持。这将可以根据需要灵活对 DomainService 类分段,使它更符合逻辑。有关更多信息,请参见共享实体主题。

在表示模型中,您可以为不直接与数据源表结构关联的表示层生成类型。例如,您可以生成名为 CustomerPresentation 的数据类型,该类型基于 Customer、CustomerAddress 和 Address 表的数据类。在表示类型中,您只能聚合与表示层相关的值。如果在数据储存库中进行更改,您可以只更改表示类型而不更新与数据交互的代码客户端应用程序。RIA Services 允许您通过表示类型更新数据。有关更多信息,请参见表示模型

最后,在您的应用程序中,您可能需要显示各种数据源的数据或将单个实体公开给多个域服务。RIA Services 通过支持不同 DomainContext 类型的实体之间的引用,允许实现此方案。例如,电子商务网站可能需要将自己订单处理系统中的数据与来自第三方域服务的产品集成。有关更多信息,请参见演练:在多个域服务间共享实体

数据批注和验证

当您使用自己的 RIA Services 应用程序中的数据类时,可以将属性应用到类或成员以指定验证规则、指定数据如何显示以及设置实体类之间的关系。System.ComponentModel.DataAnnotations 命名空间包含作为数据属性的类。通过将这些属性应用到数据类或成员,您可以集中进行数据定义,不必在多个位置重新应用相同的规则。将数据批注属性分为三个类别:验证属性、显示属性和数据建模属性。有关更多信息,请参见 Using Data Annotations to Customize Data Classes(使用数据批注以自定义数据类)和如何:验证数据。为了进行验证,您可以使用以下属性:

  1. DataTypeAttribute

  2. RangeAttribute

  3. RegularExpressionAttribute

  4. RequiredAttribute

  5. StringLengthAttribute

  6. CustomValidationAttribute

使用自动生成的数据类(如实体数据模型或 LINQ to SQL 类)时,不直接将属性应用到生成的类,因为属性在下次重新生成该类时将丢失。您可以为数据类创建元数据类,并将属性应用到元数据类。元数据类是从数据类指定为元数据类型的分部类。有关更多信息,请参见如何:添加元数据类

下面的示例显示了将 RoundtripOriginalAttributeRequiredAttributeStringLengthAttributeExcludeAttribute 特性应用于某些属性的元数据类:

<MetadataTypeAttribute(GetType(Address.AddressMetadata))>  _
Partial Public Class Address
    
    Friend NotInheritable Class AddressMetadata
        
        'Metadata classes are not meant to be instantiated.
        Private Sub New()
            MyBase.New
        End Sub
        
        Public AddressID As Integer

        <Required()> _
        <StringLength(60)> _
        <RoundtripOriginal()> _
        Public AddressLine1 As String

        <RoundtripOriginal()> _
        Public AddressLine2 As String

        <Required()> _
        <StringLength(30)> _
        <RoundtripOriginal()> _
        Public City As String

        <RoundtripOriginal()> _
        Public CountryRegion As String
        
        Public CustomerAddresses As EntityCollection(Of CustomerAddress)

        <RoundtripOriginal()> _
        Public ModifiedDate As DateTime

        <Required()> _
        <RoundtripOriginal()> _
        Public PostalCode As String

        <Exclude()> _
        Public rowguid As Guid

        <RoundtripOriginal()> _
        Public StateProvince As String
    End Class
End Class
[MetadataTypeAttribute(typeof(Address.AddressMetadata))]
public partial class Address
{

    internal sealed class AddressMetadata
    {
        // Metadata classes are not meant to be instantiated.
        private AddressMetadata()
        {
        }

        public int AddressID { get; set; }

        [Required]
        [StringLength(60)]
        [RoundtripOriginal]
        public string AddressLine1 { get; set; }

        [RoundtripOriginal]
        public string AddressLine2 { get; set; }

        [Required]
        [StringLength(30)]
        [RoundtripOriginal]
        public string City { get; set; }

        [RoundtripOriginal]
        public string CountryRegion { get; set; }

        public EntityCollection<CustomerAddress> CustomerAddresses { get; set; }

        [RoundtripOriginal]
        public DateTime ModifiedDate { get; set; }

        [Required]
        [RoundtripOriginal]
        public string PostalCode { get; set; }

        [Exclude]
        public Guid rowguid { get; set; }

        [RoundtripOriginal]
        public string StateProvince { get; set; }
    }
}

您可以通过添加共享代码文件并在该文件中创建实现验证逻辑的类,来创建自定义验证属性。在您定义自定义验证类时,必须至少提供并非自动实现的属性的某些代码,以便在客户端项目中正确生成该类。有关示例,请参见如何:验证数据

Entity 类实现 INotifyDataErrorInfo 接口。此接口定义提供同步和异步验证支持的成员。通过 INotifyDataErrorInfo 接口,将验证错误传送到客户端项目而不引发异常。有关 INotifyDataErrorInfo 的更多信息,请参见 INotifyDataErrorInfo Interface(INotifyDataErrorInfo 接口)。

您通过创建 ValidationResult 类的实例返回验证检查的结果。

下面的示例显示一个通过 ValidationResult 类的实例返回结果的自定义验证类。

Imports System
Imports System.ComponentModel.DataAnnotations

Public Module GenderValidator
    Public Function IsGenderValid(ByVal gender As String, ByVal context As ValidationContext) As ValidationResult
        If gender = "M" OrElse gender = "m" OrElse gender = "F" OrElse gender = "f" Then
            Return ValidationResult.Success
        Else
            Return New ValidationResult("The Gender field only has two valid values 'M'/'F'", New String() {"Gender"})
        End If
    End Function
End Module
using System;
using System.ComponentModel.DataAnnotations;

namespace HRApp.Web
{
    public static class GenderValidator
    {
        public static ValidationResult IsGenderValid(string gender, ValidationContext context)
        {
            if (gender == "M" || gender == "m" || gender == "F" || gender == "f")
            {
                return ValidationResult.Success;
            }
            else
            {
                return new ValidationResult("The Gender field only has two valid values 'M'/'F'", new string[] { "Gender" });
            }
        }
    }
}

数据并发

WCF RIA Services 支持开放式并发以便确保数据一致性,并且依赖于开发人员提供用于处理在更新数据源时可能会发生的潜在冲突的逻辑。当允许用户更新或删除数据时,您应该确保数据源中的数据尚未被另一个进程更改。

默认情况下,RIA Services 不为检查数据并发而将整个原始实体随已更改的值传递到数据访问层。RIA Services 而是仅存储和传递回用 RoundtripOriginalAttribute 特性标记的那些成员。此实现允许您通过仅指定那些要参与并发检查的成员来优化应用程序的性能。

在使用实体框架时,该行为时通过给元数据类中的属性或元数据类本身应用特性来实现的。在使用 POCO 定义的数据模型时,它们还可以直接应用于 CLR 类型的属性或类。有关更多信息,请参见如何:添加元数据类

事务

RIA Services 框架不自动创建事务,但您可以在提交更改时添加显式事务。为了创建自己的显式事务,您重写了 Submit 方法。有关更多信息,请参见如何:将显式事务添加到域服务

另请参见

概念

WCF RIA Services 的安全性