先日、DomainServiceの生成処理をのっとる方法を紹介しました。
これを応用してDomainServiceにMEFを使ってDIしてしまおうと思います。ということで早速MEFRIAServicesというSilverlightアプリケーションを作ります。そして、System.ComponentModel.Compositionを参照に追加します。
参照を追加したら、DomainServiceにDIするための部品を先に作ります。とりあえずEmployeeクラスのデータをとってくるだけのシンプルなEmployeeRepositoryを作成しました。
/// <summary> /// DomainServiceから返すデータの入れ物 /// </summary> public class Employee { [Key] public int ID { get; set; } public string Name { get; set; } } /// <summary> /// リポジトリのインターフェース /// </summary> /// <typeparam name="T"></typeparam> [InheritedExport] public interface IRepository<T> { IQueryable<Employee> GetEmployees(); } /// <summary> /// Employee用リポジトリ /// </summary> public class EmployeeRepository : IRepository<Employee> { /// <summary> /// とりあえずダミーデータを返す /// </summary> /// <returns></returns> public IQueryable<Employee> GetEmployees() { return new[] { new Employee { ID = 1, Name = "田中 太郎" }, new Employee { ID = 2, Name = "田中 二郎" }, new Employee { ID = 3, Name = "田中 三郎" }, } .AsQueryable(); } }
別段難しいことはしていません。IRepository
次に、DomainServiceを新規作成します。名前はEmployeeDomainServiceにしました。EmployeeDomainServiceクラスはコンストラクタでIRepository
[EnableClientAccess] [Export] [PartCreationPolicy(CreationPolicy.NonShared)] public class EmployeeDomainService : DomainService { private IRepository<Employee> emps; /// <summary> /// MEFからDIしてもらう /// </summary> /// <param name="emps"></param> [ImportingConstructor] public EmployeeDomainService(IRepository<Employee> emps) { this.emps = emps; } public IQueryable<Employee> GetEmployees() { return this.emps.GetEmployees(); } }
DomainServiceは、毎回インスタンスが作られるようにしたいのでPartCreationPolicyでNonSharedを指定しました。
そして、ファクトリクラスを用意します。こいつはコンストラクタで受け取ったコンテナからDomainServiceを取得して初期化して返すという動きをします。
namespace MEFRIAServices.Web { using System; using System.Collections.Generic; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System.ServiceModel.DomainServices.Server; public class MEFDomainServiceFactory : IDomainServiceFactory { // ジェネリックじゃないGetExportedValueメソッド private static MethodInfo nonGenericMethod = typeof(CompositionContainer) .GetMethod("GetExportedValue", Type.EmptyTypes); // GetExportedValue<T>のMethodInfoのキャッシュ private static Dictionary<Type, MethodInfo> methodCache = new Dictionary<Type,MethodInfo>(); // MEFのコンテナ private CompositionContainer container; // コンテナをコンストラクタで渡してもらう public MEFDomainServiceFactory(CompositionContainer container) { this.container = container; } public DomainService CreateDomainService(Type domainServiceType, DomainServiceContext context) { // GetExporetedValue<T>メソッドを呼んでDomainServiceを生成する var service = MakeGenericMethod(domainServiceType) .Invoke(container, null) as DomainService; // 初期化 service.Initialize(context); return service; } public void ReleaseDomainService(DomainService domainService) { domainService.Dispose(); } private MethodInfo MakeGenericMethod(Type domainServiceType) { lock (methodCache) { // キャッシュ済みならキャッシュしている値を返す if (methodCache.ContainsKey(domainServiceType)) { return methodCache[domainServiceType]; } // GetExporetedValue<T>のキャッシュをする var method = nonGenericMethod.MakeGenericMethod(domainServiceType); methodCache[domainServiceType] = method; return method; } } } }
そして、Global.asaxで、上で作成したファクトリを使うように設定します。
namespace MEFRIAServices.Web { using System; using System.ComponentModel.Composition.Hosting; using System.Reflection; using System.ServiceModel.DomainServices.Server; public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { // コンテナを作成 var container = new CompositionContainer( new AssemblyCatalog(Assembly.GetExecutingAssembly())); // ファクトリの設定 DomainService.Factory = new MEFDomainServiceFactory(container); } // 間のメソッドは省略 protected void Application_End(object sender, EventArgs e) { DomainService.Factory = null; } } }
早速使ってみます。DomainServiceの動きが確認できればいいだけなので、データソースからMainPageにEmployeeをドラッグアンドドロップしてDataGridを作成します。
そして、おもむろに実行します!実行すると、EmployeeRepositoryクラスが返しているダミーデータが表示されます。ちゃんとDomainServiceにDIされてるみたいですね。
ということで、以上でWCF RIA Services + MEFの連携が出来ることが確認できました。めでたしめでたし。