Data

[WCF RIA Services Version 1 Service Pack 2 is compatible with either .NET framework 4 or .NET Framework 4.5, and with either Silverlight 4 or Silverlight 5.]

This section describes how WCF RIA Services deals with scenarios involving modeling data, checking the validity of the data, and ensuring data concurrency. When you provide an interface for updating, deleting, or creating data from the client of a Rich Internet Application (RIA), you frequently need to model some complicated data relationships, and ensure that the data from the user is valid and still current with data in the data source before committing the data changes.

Typically, you use the Entity Data Model or LINQ to SQL to model data that exists in a relational database. However, you are not required to use a database in a RIA Services project. You can use any type of object to store data. The code in the client project that facilitates data operations is data source agnostic in the sense that it is actually unaware of the data access technology or of schema used by the middle tier.

Data Relationships

RIA Services provides features that enable you to interact with complicated data relationships, such as, hierarchical models, polymorphic inheritance models, presentation models that consolidate values from many entities, and models that include values from more than one domain service. The hierarchical model represents a parent and child composite relationship, such as Order and OrderDetails, or a recursive relationship, such as an Employee model that includes a field for a ManagerID that points to another entity in the Employee model. For more information, see Compositional Hierarchies.

In an inheritance model, you can represent a data structure that includes a Customer entity and two entities that derive from it: PublicSectorCustomer and PrivateSectorCustomer. Using domain operations, you can query and update the types. For more information, see Inheritance in Data Models.

Support for non-entity complex types has been added in RIA Services V1.0 SP1. Specifically, support is provided for codegen, metadata, deep validation, change tracking, edit sessions and complex type parameters. This means that custom types, such as Address, can now be used as entity properties or parameters or return values from DomainService methods. For more information, see the Complex Types topics.

Support for sharing an entity with multiple domain services has been added in RIA Services V1.0 SP1. This enables the flexibility needed to segment your DomainService classes more logically. For more information, see the Shared Entities topic.

In a presentation model, you can build types for the presentation tier that are not tied directly to the structure of the data source tables. For example, you can build a data type named CustomerPresentation that is based on data classes for Customer, CustomerAddress, and Address tables. In the presentation type, you aggregate only the values that are relevant to the presentation tier. If changes are made in the data repository, you can change only the presentation type and not update the code client application that interacts with the data. RIA Services enables you to update data through the presentation type. For more information, see Presentation Models.

Finally, in your application, you may need to display data from a variety of data sources or expose a single entity to more than one domain service. RIA Services enables this scenario by supporting references between entities from different DomainContext types. For example, an e-commerce Web site may need to integrate data from its order processing system with products from a third-party domain service. For more information, see Walkthrough: Sharing Entities between Multiple Domain Services.

Data Annotations and Validation

When you use data classes in your RIA Services application, you can apply attributes to the class or members that specify validation rules, specify how the data is displayed, and set relationships between entity classes. The System.ComponentModel.DataAnnotations namespace contains the classes that are used as data attributes. By applying these attributes on the data class or member, you centralize the data definition and you do not have to re-apply the same rules in multiple locations. The data annotation attributes are organized into three categories: validation attributes, display attributes, and data modeling attributes. For more information, see Using Data Annotations to Customize Data Classes and How to: Validate Data. For validation, you can use the following attributes:

  1. DataTypeAttribute

  2. RangeAttribute

  3. RegularExpressionAttribute

  4. RequiredAttribute

  5. StringLengthAttribute

  6. CustomValidationAttribute

When working with data classes that are automatically generated, such as an Entity Data Model or LINQ to SQL classes, you do not apply the attributes directly to the generated classes because the attributes will be lost the next time the class is re-generated. Instead, you create a metadata class for the data class, and apply the attributes to the metadata class. A metadata class is a partial class that is designated from the data class as the metadata type. For more information, see How to: Add Metadata Classes.

The following example shows a metadata class with RoundtripOriginalAttribute, RequiredAttribute, StringLengthAttribute, and ExcludeAttribute attributes applied to some of the properties.

<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)> _
        Public AddressLine1 As String

        Public AddressLine2 As String

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

        Public CountryRegion As String

        Public CustomerAddresses As EntityCollection(Of CustomerAddress)

        Public ModifiedDate As DateTime

        <Required()> _
        Public PostalCode As String

        <Exclude()> _
        Public rowguid As Guid

        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;

        [Required]
        [StringLength(60)]
        public string AddressLine1;

        public string AddressLine2;

        [Required]
        [StringLength(30)]
        public string City;

        public string CountryRegion;

        public EntityCollection<CustomerAddress> CustomerAddresses;

        public DateTime ModifiedDate;

        [Required]
        public string PostalCode;

        [Exclude]
        public Guid rowguid;

        public string StateProvince;
    }
}

You can create a customized validation attribute by adding a shared code file and in that file creating a class that implements the validation logic. When you define the customized validation class, you must provide at least some code other than auto-implemented properties for the class to be correctly generated in the client project. For an example, see How to: Validate Data.

The Entity class implements the INotifyDataErrorInfo interface. This interface defines members that provide synchronous and asynchronous validation support. With the INotifyDataErrorInfo interface, validation errors are communicated to the client project without raising an exception. For more information about INotifyDataErrorInfo, see INotifyDataErrorInfo Interface.

You return the result of a validation check by creating an instance of the ValidationResult class.

The following example shows a customized validation class that returns the results through an instance of the ValidationResult class.

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" });
            }
        }
    }
}

Data Concurrency

WCF RIA Services supports optimistic concurrency to insure data consistency and relies on developers to provide the logic for handling potential conflicts that can occur when updating a data source. When you enable users to update or delete data, you should make sure that the data in the data source has not been changed by another process

By default, RIA Services does not pass the entire original entity, along with the changed values, to the data access layer to check for data concurrency. Instead, RIA Services stores and passes back only those members that are marked with the RoundtripOriginalAttribute attribute. This implementation enables you to optimize the performance of your application by specifying only those members that you want to participate in the concurrency check.

The behavior is implemented by applying the attribute to properties in a metadata class, or to the metadata class itself, or to the metadata classes themselves, when working with the Entity Framework. They can also be applied directly to properties or classes of CLR types when working with POCO-defined data models. For more information, see How to: Add Metadata Classes.

Transactions

The RIA Services framework does not automatically create transactions, but you can add explicit transactions when submitting changes. To create your own explicit transaction, you override the Submit method. For more information, see How to: Add Explicit Transactions to a Domain Service.

See Also

Concepts

Building Secure Applications with WCF RIA Services