프레젠테이션 모델
WCF RIA Services를 사용하면 프레젠테이션 모델이라고 하는 데이터 액세스 계층의 여러 엔터티로부터 데이터를 집계하는 데이터 모델을 만들 수 있습니다. 데이터 액세스 계층의 엔터티를 클라이언트에 직접 노출하지 않으려는 경우 이 기능을 사용합니다. 프레젠테이션 모델을 사용할 때 클라이언트는 변경하지 않고 프레젠테이션 모델만 변경하여 데이터 액세스 계층의 변경 내용에 응답할 수 있습니다. 또한 클라이언트의 사용자와 관련된 필드만 집계하는 모델을 디자인하여 클라이언트 코드를 단순화할 수 있습니다. 이 항목에서는 프레젠테이션 모델을 만들고, 쿼리하고, 업데이트하는 방법과 중간 계층이나 데이터 소스에 변경 내용이 설정된 경우 클라이언트로 값을 다시 전달하는 방법에 대해 설명합니다.
프레젠테이션 모델 만들기
데이터 무결성을 유지하는 데 필요한 데이터베이스 구조는 클라이언트 응용 프로그램의 엔터티에 필요한 데이터베이스 구조보다 더 복잡할 수 있습니다. 응용 프로그램과 관련된 필드를 하나의 프레젠테이션 모델로 결합하여 이 데이터 구조를 단순화하는 프레젠테이션 모델을 만들 수 있습니다. 예를 들어, 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 Services 에서는 엔터티의 값을 프레젠테이션 모델에 다시 매핑하는 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;
}