かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

WCF RIA Servicesで複雑なエンテティを戻り値にする方法

自分で作ったクラスをエンテティとして扱うDomain Serviceを公開する場合に、プロパティがプリミティブ型だけで構成される場合は、主キーとなるプロパティにKey属性をつけるだけで普通に使えます。
しかし、プロパティの型が、さらに別のクラスだったりする場合は、プリミティブ型などのときのようにはすんなりいきません。


例としては、以下のようなクラス構造があげられます。

クラス間の関連の線は引かれてませんが、EmployeeクラスのDepartmentプロパティはDepartmentクラスです。DepartmentクラスのEmployeesプロパティは、List型です。このような構造を持ったクラスをDomain Serviceのエンテティとして使うと、普通に作った場合EmployeeクラスのDepartmentプロパティとDepartmentクラスのEmployeesプロパティは、Silverlight側まで伝搬されません。

では、どうやれば伝搬されるかというと、該当するプロパティにInclude属性とAssociation属性をつけてやる必要があります。Association属性は、名前、このプロパティを持つクラス側のキー列名、関連するクラスのキー列名を指定します。
例えば、EmployeeクラスのDepartmentプロパティでは、DepartmentIDプロパティと、DepartmentクラスのIDプロパティで関連付けをやってるので、以下のようになります。

namespace SilverlightApplication8.Web
{
    using System.ComponentModel.DataAnnotations;
    using System.ServiceModel.DomainServices.Server;

    public class Employee
    {
        [Key]
        public long ID { get; set; }

        public string Name { get; set; }

        public long DepartmentID { get; set; }

        [Include]
        [Association("Department", "DepartmentID", "ID")]
        public Department Department { get; set; }
    }
}


同じ要領で、Departmentクラスは以下のようになります。

namespace SilverlightApplication8.Web
{
    using System.ComponentModel.DataAnnotations;
    using System.Collections.Generic;
    using System.ServiceModel.DomainServices.Server;

    public class Department
    {
        [Key]
        public long ID { get; set; }

        public string Name { get; set; }

        [Include]
        [Association("Employees", "ID", "DepartmentID")]
        public List<Employee> Employees { get; set; }
    }
}

これで、EmployeeクラスのDepartmentプロパティやDepartmentクラスのEmployeesプロパティもきちんとSilverlight側まで伝搬されます。実際にこのクラスを公開するDomainServiceのコードは、以下のような感じになります。

namespace SilverlightApplication8.Web
{
    using System.Linq;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;
    using System.Collections.Generic;

    [EnableClientAccess()]
    public class EmployeesDomainService : DomainService
    {
        public IQueryable<Employee> GetEmployees()
        {
            return new[]
            {
                new Employee 
                { 
                    ID = 1, 
                    Name = "田中 太郎", 
                    DepartmentID = 1,
                    Department = new Department { ID = 1, Name = "総務部" } 
                },
                new Employee 
                { 
                    ID = 2, 
                    Name = "田中 次郎", 
                    DepartmentID = 2,
                    Department = new Department { ID = 2, Name = "人事部" } 
                },
                new Employee 
                { 
                    ID = 3, 
                    Name = "田中 三郎", 
                    DepartmentID = 2,
                    Department = new Department { ID = 2, Name = "人事部" } 
                },
            }.AsQueryable();
        }

        public IQueryable<Department> GetDepartments()
        {
            return new[]
            {
                new Department
                {
                    ID = 1,
                    Name = "総務部",
                    Employees = new List<Employee>
                    {
                        new Employee { ID = 1, Name = "太郎", DepartmentID = 1 },
                        new Employee { ID = 2, Name = "次郎", DepartmentID = 1 },
                        new Employee { ID = 3, Name = "三郎", DepartmentID = 1 },
                    }
                }, 
                new Department
                {
                    ID = 2,
                    Name = "人事部",
                    Employees = new List<Employee>
                    {
                        new Employee { ID = 4, Name = "四郎", DepartmentID = 2 },
                        new Employee { ID = 5, Name = "五郎", DepartmentID = 2 },
                        new Employee { ID = 6, Name = "六郎", DepartmentID = 2 },
                    }
                }
            }.AsQueryable();
        }
    }
}

このDomainServiceからデータを取得して、DataGridにデータを表示するアプリケーションを作ってみました。(といってもデータソースからドラッグアンドドロップしただけです)
一番上のDataGridがGetEmployeesメソッドの戻り値を表示していて、二番目のDataGridがGetDepartmentsメソッドの戻り値を表示しています。そして、三番目のDataGridが、二番目のDataGridで選択された行のEmployeesプロパティのデータを表示しています。

EmployeeクラスのDepartmentプロパティのNameプロパティがきちんととれていたり、選択したDepartmentのEmployeesプロパティの内容がきちんとDataGridに表示されています。

このコードは、下のリンクからダウンロードできます。
WCF_RIA_Services_AssociationSample.zip