プレゼンテーション モデル
WCF RIA サービス を使用すると、プレゼンテーション モデルと呼ばれるデータ アクセス層内の複数のエンティティからデータを集計するデータ モデルを作成できます。この機能は、データ アクセス層内のエンティティをクライアントに直接公開しないようにする場合に使用します。プレゼンテーション モデルを使用すると、クライアントではなくプレゼンテーション モデルのみを変更することで、データ アクセス層での変更に対応できます。また、クライアントのユーザーに関連するフィールドだけを集計するモデルをデザインすることで、クライアント コードを簡略化できます。ここでは、プレゼンテーション モデルを作成、照会、および更新する方法と、中間層またはデータ ソースで変更が設定されている場合にクライアントに値を渡す方法について説明します。
プレゼンテーション モデルの作成
データの整合性を維持するために必要なデータベース構造は、クライアント アプリケーションのエンティティに必要な構造よりも複雑なことがあります。アプリケーションに関連する複数のフィールドを結合して 1 つのプレゼンテーション モデルにすることで、このデータ構造を簡略化するプレゼンテーション モデルを作成できます。たとえば、AdventureWorksLT サンプル データベースでは、Customer
、CustomerAddress
、および Address
の各テーブルを使用して顧客データと住所データを取得します。
プレゼンテーション モデルを作成するには、サーバー プロジェクトにクラスを作成し、使用できるようにするプロパティを定義します。定義するプロパティは、エンティティから公開するプロパティに対応します。たとえば、サーバー プロジェクト内に次の CustomerPresentationModel
クラスを作成して、Customer
、CustomerAddress
、および Address
の各テーブルから必要なフィールドのみを表示できます。
Public Class CustomerPresentationModel
<Key()> _
Public Property CustomerID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Property EmailAddress As String
Public Property Phone As String
Public Property AddressType As String
Public Property AddressLine1 As String
Public Property AddressLine2 As String
Public Property City As String
Public Property StateProvince As String
Public Property PostalCode As String
Public Property AddressID As Integer
Public Property AddressModifiedDate As DateTime
Public Property CustomerModifiedDate As DateTime
End Class
public class CustomerPresentationModel
{
[Key]
public int CustomerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string Phone { get; set; }
public string AddressType { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string StateProvince { get; set; }
public string PostalCode { get; set; }
public int AddressID { get; set; }
public DateTime AddressModifiedDate { get; set; }
public DateTime CustomerModifiedDate { get; set; }
}
プレゼンテーション モデルでの値のクエリと変更
プレゼンテーション モデルを作成した後、作成したプレゼンテーション モデルをクライアント プロジェクトに公開するには、プレゼンテーションの種類と対話するドメイン サービスを追加します。エンティティの値はこのドメイン サービスを介してのみ公開され、エンティティ全体を公開するドメイン サービスを介して公開されることはありません。次の例は、DomainService クラスから派生するドメイン サービスを示しています。
[EnableClientAccess()]
public class CustomerDomainService : DomainService
{
AdventureWorksLT_DataEntities context = new AdventureWorksLT_DataEntities();
}
データを取得するには、ドメイン サービスにクエリ メソッドを追加します。クエリ メソッドで、データ アクセス層のエンティティから関連するデータを取得し、その値を、プレゼンテーション モデルの新しいインスタンス内の対応するプロパティに設定します。クエリ メソッドから、プレゼンテーション モデル型のインスタンス、またはジェネリック型が CustomerPresentationModel
型である IQueryable’1 を返します。次の例では、顧客のプレゼンテーション モデルのクエリ メソッドを示しています。
Public Function GetCustomersWithMainOffice() As IQueryable(Of CustomerPresentationModel)
Return From c In context.Customers
Join ca In context.CustomerAddresses On c.CustomerID Equals ca.CustomerID
Join a In context.Addresses On ca.AddressID Equals a.AddressID
Where ca.AddressType = "Main Office"
Select New CustomerPresentationModel() With _
{
.CustomerID = c.CustomerID,
.FirstName = c.FirstName,
.LastName = c.LastName,
.EmailAddress = c.EmailAddress,
.Phone = c.Phone,
.AddressType = ca.AddressType,
.AddressLine1 = a.AddressLine1,
.AddressLine2 = a.AddressLine2,
.City = a.City,
.StateProvince = a.StateProvince,
.PostalCode = a.PostalCode,
.AddressID = a.AddressID,
.AddressModifiedDate = a.ModifiedDate,
.CustomerModifiedDate = c.ModifiedDate
}
End Function
public IQueryable<CustomerPresentationModel> GetCustomersWithMainOffice()
{
return from c in context.Customers
join ca in context.CustomerAddresses on c.CustomerID equals ca.CustomerID
join a in context.Addresses on ca.AddressID equals a.AddressID
where ca.AddressType == "Main Office"
select new CustomerPresentationModel()
{
CustomerID = c.CustomerID,
FirstName = c.FirstName,
LastName = c.LastName,
EmailAddress = c.EmailAddress,
Phone = c.Phone,
AddressType = ca.AddressType,
AddressLine1 = a.AddressLine1,
AddressLine2 = a.AddressLine2,
City = a.City,
StateProvince = a.StateProvince,
PostalCode = a.PostalCode,
AddressID = a.AddressID,
AddressModifiedDate = a.ModifiedDate,
CustomerModifiedDate = c.ModifiedDate
};
}
データ アクセス層の Customer
、CustomerAddress
、および Address
の各エンティティはドメイン サービスによって公開されていないため、その型はクライアント プロジェクトでは生成されません。代わりに、CustomerPresentationModel
型のみがクライアント プロジェクトで生成されます。
プレゼンテーション モデルを使用してデータを更新するには、更新メソッドを作成し、プレゼンテーション モデルの値をエンティティに保存するためのロジックを定義します。更新メソッドの例は、次のセクションの最後に示します。
クライアントへの値の受け渡し
変更を送信した後、中間層ロジックまたはデータ ソースで設定されている値をクライアントに渡すことができます。RIA サービス には、エンティティの値をプレゼンテーション モデルに再度マップするための Associate メソッドが用意されています。このメソッドで、変更が送信された後に呼び出されるコールバック メソッドを指定します。コールバック メソッドでは、中間層で変更された値をプレゼンテーション モデルに割り当てます。クライアントが現在の値を処理していることを確認するには、この手順を実行します。
次の例では、エンティティの値を更新する方法と、変更されたデータをプレゼンテーション モデルに再度マップする方法を示しています。
<Update()> _
Public Sub UpdateCustomer(ByVal customerPM As CustomerPresentationModel)
Dim customerEntity As Customer = context.Customers.Where(Function(c) c.CustomerID = customerPM.CustomerID).FirstOrDefault()
Dim customerAddressEntity As CustomerAddress = context.CustomerAddresses.Where(Function(ca) ca.CustomerID = customerPM.CustomerID And ca.AddressID = customerPM.AddressID).FirstOrDefault()
Dim addressEntity As Address = context.Addresses.Where(Function(a) a.AddressID = customerPM.AddressID).FirstOrDefault()
customerEntity.FirstName = customerPM.FirstName
customerEntity.LastName = customerPM.LastName
customerEntity.EmailAddress = customerPM.EmailAddress
customerEntity.Phone = customerPM.Phone
customerAddressEntity.AddressType = customerPM.AddressType
addressEntity.AddressLine1 = customerPM.AddressLine1
addressEntity.AddressLine2 = customerPM.AddressLine2
addressEntity.City = customerPM.City
addressEntity.StateProvince = customerPM.StateProvince
addressEntity.PostalCode = customerPM.PostalCode
Dim originalValues As CustomerPresentationModel = Me.ChangeSet.GetOriginal(customerPM)
If (originalValues.FirstName <> customerPM.FirstName Or
originalValues.LastName <> customerPM.LastName Or
originalValues.EmailAddress <> customerPM.EmailAddress Or
originalValues.Phone <> customerPM.Phone) Then
customerEntity.ModifiedDate = DateTime.Now
End If
If (originalValues.AddressLine1 <> customerPM.AddressLine1 Or
originalValues.AddressLine2 <> customerPM.AddressLine2 Or
originalValues.City <> customerPM.City Or
originalValues.StateProvince <> customerPM.StateProvince Or
originalValues.PostalCode <> customerPM.PostalCode) Then
addressEntity.ModifiedDate = DateTime.Now
End If
context.SaveChanges()
Me.ChangeSet.Associate(customerPM, customerEntity, AddressOf MapCustomerToCustomerPM)
Me.ChangeSet.Associate(customerPM, addressEntity, AddressOf MapAddressToCustomerPM)
End Sub
Private Sub MapCustomerToCustomerPM(ByVal customerPM As CustomerPresentationModel, ByVal customerEntity As Customer)
customerPM.CustomerModifiedDate = customerEntity.ModifiedDate
End Sub
Private Sub MapAddressToCustomerPM(ByVal customerPM As CustomerPresentationModel, ByVal addressEntity As Address)
customerPM.AddressModifiedDate = addressEntity.ModifiedDate
End Sub
[Update]
public void UpdateCustomer(CustomerPresentationModel customerPM)
{
Customer customerEntity = context.Customers.Where(c => c.CustomerID == customerPM.CustomerID).FirstOrDefault();
CustomerAddress customerAddressEntity = context.CustomerAddresses.Where(ca => ca.CustomerID == customerPM.CustomerID && ca.AddressID == customerPM.AddressID).FirstOrDefault();
Address addressEntity = context.Addresses.Where(a => a.AddressID == customerPM.AddressID).FirstOrDefault();
customerEntity.FirstName = customerPM.FirstName;
customerEntity.LastName = customerPM.LastName;
customerEntity.EmailAddress = customerPM.EmailAddress;
customerEntity.Phone = customerPM.Phone;
customerAddressEntity.AddressType = customerPM.AddressType;
addressEntity.AddressLine1 = customerPM.AddressLine1;
addressEntity.AddressLine2 = customerPM.AddressLine2;
addressEntity.City = customerPM.City;
addressEntity.StateProvince = customerPM.StateProvince;
addressEntity.PostalCode = customerPM.PostalCode;
CustomerPresentationModel originalValues = this.ChangeSet.GetOriginal(customerPM);
if (originalValues.FirstName != customerPM.FirstName ||
originalValues.LastName != customerPM.LastName ||
originalValues.EmailAddress != customerPM.EmailAddress ||
originalValues.Phone != customerPM.Phone)
{
customerEntity.ModifiedDate = DateTime.Now;
}
if (originalValues.AddressLine1 != customerPM.AddressLine1 ||
originalValues.AddressLine2 != customerPM.AddressLine2 ||
originalValues.City != customerPM.City ||
originalValues.StateProvince != customerPM.StateProvince ||
originalValues.PostalCode != customerPM.PostalCode)
{
addressEntity.ModifiedDate = DateTime.Now;
}
context.SaveChanges();
this.ChangeSet.Associate(customerPM, customerEntity, MapCustomerToCustomerPM);
this.ChangeSet.Associate(customerPM, addressEntity, MapAddressToCustomerPM);
}
private void MapCustomerToCustomerPM(CustomerPresentationModel customerPM, Customer customerEntity)
{
customerPM.CustomerModifiedDate = customerEntity.ModifiedDate;
}
private void MapAddressToCustomerPM(CustomerPresentationModel customerPM, Address addressEntity)
{
customerPM.AddressModifiedDate = addressEntity.ModifiedDate;
}