かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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