データ
このセクションでは、データのモデル化、データの妥当性の確認、およびデータの同時実行を必要とするシナリオが WCF RIA サービス によってどのように処理されるかについて説明します。リッチ インターネット アプリケーション (RIA) のクライアントでデータを更新、削除、または作成するインターフェイスを提供する場合、一部の複雑なデータ リレーションシップをモデル化し、ユーザーのデータが有効かつデータ ソース内のデータと同じ最新の状態になっていることを確認したうえで、データの変更をコミットすることが必要になる場合がよくあります。
通常、リレーショナル データベースに存在するデータをモデル化するには、Entity Data Model または LINQ to SQL を使用します。ただし、RIA サービス プロジェクトで必ずしもデータベースを使用する必要はありません。任意の種類のオブジェクトを使用してデータを格納できます。データ操作を容易にするクライアント プロジェクトのコードは、データ アクセス テクノロジや、中間層で使用されるスキーマを実際には認識しないという点で、データ ソースに依存しないと言えます。
データ リレーションシップ
RIA サービス には、階層モデル、ポリモーフィックな継承モデル、多くのエンティティからの値を統合するプレゼンテーション モデル、複数のドメイン サービスの値を含むモデルなどの複雑なデータ リレーションシップとの対話を可能にする機能が用意されています。階層モデルは、親と子の複合リレーションシップ (Order や OrderDetails など)、または再帰的リレーションシップ (Employee モデルの別のエンティティを参照する ManagerID のフィールドを含む Employee モデルなど) を表します。詳細については、「合成階層」を参照してください。
継承モデルでは、Customer エンティティと、そのエンティティから派生する 2 つのエンティティ (PublicSectorCustomer および PrivateSectorCustomer) を含むデータ構造を表すことができます。ドメイン操作を使用して、これらの型の照会および更新を行うことができます。詳細については、「データ モデルでの継承」を参照してください。
RIA サービス V1.0 SP1 には、エンティティ型以外の複合型のサポートが追加されました。具体的には、コード生成、メタデータ、詳細な検証、変更の追跡、編集セッション、および複合型パラメーターがサポートされています。つまり、Address などのカスタム型は、エンティティ プロパティやパラメーター、または DomainService メソッドからの戻り値として使用できるようになりました。詳細については、「複合型」を参照してください。
RIA サービス V1.0 SP1 では、複数のドメイン サービスで 1 つのエンティティを共有できるようになりました。これにより、DomainService クラスをより論理的に分割するために必要な柔軟性が実現します。詳細については、「共有エンティティ」を参照してください。
プレゼンテーション モデルでは、データ ソース テーブルの構造に直接関連付けられていないプレゼンテーション層の型を作成できます。たとえば、Customer テーブル、CustomerAddress テーブル、および Address テーブルのデータ クラスに基づく、CustomerPresentation という名前のデータ型を作成できます。プレゼンテーションの型では、プレゼンテーション層に関連する値のみを集計します。データ リポジトリに変更を加えた場合は、プレゼンテーションの型のみを変更し、データと対話するコード クライアント アプリケーションは更新しないようにすることができます。RIA サービス を使用すると、プレゼンテーションの型によってデータを更新できます。詳細については、「プレゼンテーション モデル」を参照してください。
最後に、アプリケーションでは、さまざまなデータ ソースのデータを表示したり、複数のドメイン サービスに 1 つのエンティティを公開したりすることが必要になる場合があります。RIA サービス では、さまざまな DomainContext 型のエンティティ間の参照がサポートされているため、このシナリオが可能になります。たとえば、電子商取引 Web サイトでは、サイトの注文処理システムのデータと、サード パーティのドメイン サービスの製品を統合することが必要になる場合があります。詳細については、「チュートリアル: 複数のドメイン サービス間でのエンティティの共有」を参照してください。
データの注釈と検証
RIA サービス アプリケーションでデータ クラスを使用する場合、検証規則を指定するクラスまたはメンバーに属性を適用し、データの表示方法を指定して、エンティティ クラス間のリレーションシップを設定できます。System.ComponentModel.DataAnnotations 名前空間には、データ属性として使用されるクラスが含まれています。データ クラスまたはメンバーにこれらの属性を適用してデータ定義を一元化します。これにより、同じ規則の適用を複数の場所で繰り返す必要がなくなります。データ注釈属性は、検証属性、表示属性、およびデータ モデリング属性という 3 つのカテゴリに分類されます。詳細については、「データ注釈を使用したデータ クラスのカスタマイズ」および「方法: データを検証する」を参照してください。検証には、次の属性を使用できます。
DataTypeAttribute
RangeAttribute
RegularExpressionAttribute
RequiredAttribute
StringLengthAttribute
CustomValidationAttribute
上記の属性は次回クラスが再生成されるときに失われるため、自動的に生成されるデータ クラス (Entity Data Model クラスや LINQ to SQL クラスなど) を使用して作業する場合は、生成されるクラスに直接属性を適用しないでください。代わりに、データ クラスのメタデータ クラスを作成し、メタデータ クラスに属性を適用します。メタデータ クラスは部分クラスで、メタデータ型としてデータ クラスから指定されます。詳細については、「方法: メタデータ クラスを追加する」を参照してください。
次の例は、一部のプロパティに RoundtripOriginalAttribute 属性、RequiredAttribute 属性、StringLengthAttribute 属性、および ExcludeAttribute 属性を適用したメタデータ クラスを示しています。
<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 インターフェイス」を参照してください。
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 サービス はデータの一貫性を確認するためのオプティミスティック同時実行制御をサポートしており、データ ソースの更新時に発生することのある競合を処理するロジックを開発者が提供することを前提にしています。ユーザーによるデータの更新または削除を許可する場合、別のプロセスによってデータ ソース内のデータが変更されていないことを確認する必要があります。
既定では、RIA サービス が元のエンティティ全体を変更された値と共にデータ アクセス層に渡して、データの同時実行を確認することはありません。その代わり、RIA サービス は、RoundtripOriginalAttribute 属性でマークされたメンバーのみを格納し、渡します。この実装を行うと、同時実行チェックの対象になるメンバーのみを指定できるため、アプリケーションのパフォーマンスを最適化できます。
この動作は、Entity Framework の操作時に、メタデータ クラスのプロパティ、1 つのメタデータ クラス自体、または複数のメタデータ クラス自体に属性を適用して実装されます。POCO 定義のデータ モデルを操作するときは、CLR 型のプロパティまたはクラスに直接適用することもできます。詳細については、「方法: メタデータ クラスを追加する」を参照してください。
トランザクション
RIA サービス フレームワークによってトランザクションが自動的に作成されることはありませんが、変更の送信時に明示的なトランザクションを追加できます。独自の明示的なトランザクションを作成するには、Submit メソッドをオーバーライドします。詳細については、「方法: ドメイン サービスに明示的なトランザクションを追加する」を参照してください。