Skip to content

Instantly share code, notes, and snippets.

@runceel
Created May 6, 2012 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save runceel/2622484 to your computer and use it in GitHub Desktop.
Save runceel/2622484 to your computer and use it in GitHub Desktop.
明日のBlog記事ネタ
*[WinRT][MEF]WinRTのMEFでServiceLocator実装してみた
WinRTのMEFってCompositionContainerクラスがいなくなってCompositionServiceクラスでSatisfyImportsOnceメソッド使って対象クラスに何かをImportするということしかできないっぽいです。なのでMVVM Light 4のDIコンテナにMEF使ってやろうと思ったらいきなり挫折してしまいましたorz
それじゃぁ悔しいので、無理やりCompositionServiceクラスを使った状態でIServiceLocatorインターフェースを実装してみました。ちなみにMVVM Light 4に添付されてるMicrosoft.Practices.ServiceLocation.dllを使います。
**実装
ということでさくっと実装してみました。SatisfyImportsOnceメソッドにRegistrationBuilderのインスタンスを渡せば実行時にImportの挙動とかをいじれるみたいなので、それを使ってごにょごにょっとしてます。
>|cs|
namespace SampleImplementation
{
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.ComponentModel.Composition.Registration;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.ServiceLocation;
public class MefServiceLocator : IServiceLocator
{
private CompositionService service;
public MefServiceLocator(params ComposablePartCatalog[] catalogs)
{
// カタログからCompositionServiceを作成
var catalog = new AggregateCatalog(catalogs);
this.service = catalog.CreateCompositionService();
}
private IEnumerable<object> DoGetAllInstances(Type serviceType)
{
// ManyValueHolder<T>型を作成
var holderType = typeof(ManyValueHolder<>).MakeGenericType(serviceType);
// ValuesプロパティにImportManyをつけてるイメージ
var b = new RegistrationBuilder();
b.ForType(holderType)
.ImportProperties(p => true, (p, c) => c.AsMany(true));
// ManyValueHolder<T>型のインスタンスを作成してImport
var holderInstance = Activator.CreateInstance(holderType);
this.service.SatisfyImportsOnce(holderInstance, b);
// Valuesプロパティの値を取得して返却
var valuePropertyInfo = holderType.GetTypeInfo().GetDeclaredProperty("Values");
return (IEnumerable<object>) valuePropertyInfo.GetValue(holderInstance);
}
private object DoGetInstance(Type serviceType, string key)
{
// SingleValueHolder<T>型を作成
var holderType = typeof(SingleValueHolder<>).MakeGenericType(serviceType);
// ValueプロパティにImport属性をつけてるイメージ
// コントラクト名が指定されている場合はそれも追加する
var b = new RegistrationBuilder();
b.ForType(holderType)
.ImportProperties(p => true, (p, c) =>
{
if (!string.IsNullOrEmpty(key))
{
c.AsContractName(key);
}
});
// SingleValueHolder<T>型のインスタンスを作成してImport
var holderInstance = Activator.CreateInstance(holderType);
this.service.SatisfyImportsOnce(holderInstance, b);
// Valueプロパティの値を取得して返却
var valuePropertyInfo = holderType.GetTypeInfo().GetDeclaredProperty("Value");
return valuePropertyInfo.GetValue(holderInstance);
}
public IEnumerable<TService> GetAllInstances<TService>()
{
return DoGetAllInstances(typeof(TService)).Cast<TService>();
}
public IEnumerable<object> GetAllInstances(Type serviceType)
{
return DoGetAllInstances(serviceType);
}
public TService GetInstance<TService>(string key)
{
return (TService)DoGetInstance(typeof(TService), key);
}
public TService GetInstance<TService>()
{
return (TService) DoGetInstance(typeof(TService), null);
}
public object GetInstance(Type serviceType, string key)
{
return DoGetInstance(serviceType, key);
}
public object GetInstance(Type serviceType)
{
return DoGetInstance(serviceType, null);
}
}
/// <summary>
/// 単一のインスタンスをImportするためのクラス
/// </summary>
/// <typeparam name="T"></typeparam>
class SingleValueHolder<T>
{
public T Value { get; set; }
}
/// <summary>
/// 複数のインスタンスをImportするためのクラス
/// </summary>
/// <typeparam name="T"></typeparam>
class ManyValueHolder<T>
{
public IEnumerable<T> Values { get; set; }
}
}
||<
こんな感じで実装完了!通したテストは以下のような感じ。
>|cs|
namespace MefServiceLocatorImpl.Test
{
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SampleImplementation;
[TestClass]
public class MefServiceLocatorTest
{
private MefServiceLocator locator;
[TestInitialize]
public void Initialize()
{
this.locator = new MefServiceLocator(
new AssemblyCatalog(typeof(MefServiceLocatorTest).GetTypeInfo().Assembly));
}
[TestCleanup]
public void Cleanup()
{
this.locator = null;
}
[TestMethod]
public void InitTest()
{
Assert.IsNotNull(this.locator);
}
[TestMethod]
public void SingleExportTest()
{
var target = locator.GetInstance<ExportTarget>();
Assert.IsNotNull(target);
}
[TestMethod]
public void NamedSingleExportTest()
{
var target = locator.GetInstance<NamedExportTarget>("Sample");
Assert.IsNotNull(target);
}
[TestMethod]
[ExpectedException(typeof(CompositionException))]
public void NamedSingleExportMissingTest()
{
locator.GetInstance<NamedExportTarget>("MissingName");
}
[TestMethod]
public void ManyExportTest()
{
var targets = locator.GetAllInstances<IManyExportTarget>();
Assert.IsNotNull(targets);
Assert.AreEqual(targets.Count(), 3);
}
}
[Export]
public class ExportTarget
{
}
[Export("Sample")]
public class NamedExportTarget { }
public interface IManyExportTarget { }
[Export(typeof(IManyExportTarget))]
public class ManyExportTarget1 : IManyExportTarget { }
[Export(typeof(IManyExportTarget))]
public class ManyExportTarget2 : IManyExportTarget { }
[Export(typeof(IManyExportTarget))]
public class ManyExportTarget3 : IManyExportTarget { }
}
||<
これでオールグリーン!ということでMVVM Light 4のSimpleIoCじゃなくてMEFを使えるようになりそうです。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment