さて、UnityContainerの拡張のお話。
UnityContainerExtensionを実装する形で作成するという例をちょっと前に書いた。
今日は、もうちょっと掘り下げてみようと思う。
前回の例では、UnityContainerExtensionのInitializeメソッドでフレームワークで使用するクラス群を登録しちゃえば便利そうだね~と言った。
今回は、それに加えてUnityContainerがクラスのインスタンスを生成する過程に割り込む方法を書いてみようと思う。
UnityContainerExtensionにはContextというプロパティがある。Contextの型はExtensionContextという型で、ここに色々なメソッドやプロパティが定義されている。
その中の1つにStrategiesというプロパティがある。
ここに、Microsoft.Practices.ObjectBuilder2.IBuilderStrategyを実装したクラスをAddメソッドを使って追加することで色々出来るようになる。
IBuilderStrategyインターフェースのメソッドは、以下の4つになる。
- PostBuildUp
- PostTearDown
- PreBuildUp
- PreTearDown
この中で、オブジェクトの生成に関わるメソッドは、PreBuildUpとPostBuildUpの2つになる。
~TearDownのほうは、IUnityContainerのTearDownメソッドを呼んでインスタンスを破棄するときに使われるので、今回はとりあえず無視する。
とりあえず、日本語で詳しく書かれた情報が見当たらなかったので適当に実装して動きを見てみるために適当な実装をでっちあげた。
public class MyStragety : IBuilderStrategy
{
private string stage;
public MyStragety(string stage)
{
this.stage = stage;
}
#region IBuilderStrategy メンバ
public void PostBuildUp(IBuilderContext context)
{
Print("PostBuildUp", context);
}
public void PostTearDown(IBuilderContext context)
{
// no op
}
public void PreBuildUp(IBuilderContext context)
{
Print("PreBuildUp", context);
}
public void PreTearDown(IBuilderContext context)
{
// no op
}
private void Print(string method, IBuilderContext context)
{
Console.WriteLine(stage + ", " + method);
}
#endregion
}
これを登録するExtensionも作成する。
public class MyExtension : UnityContainerExtension
{
protected override void Initialize()
{
Context.Strategies.Add(new MyStragety("Creation"), UnityBuildStage.Creation);
Context.Strategies.Add(new MyStragety("Initialization"), UnityBuildStage.Initialization);
Context.Strategies.Add(new MyStragety("Lifetime"), UnityBuildStage.Lifetime);
Context.Strategies.Add(new MyStragety("PostInitialization"), UnityBuildStage.PostInitialization);
Context.Strategies.Add(new MyStragety("PreCreation"), UnityBuildStage.PreCreation);
Context.Strategies.Add(new MyStragety("Setup"), UnityBuildStage.Setup);
Context.Strategies.Add(new MyStragety("TypeMapping"), UnityBuildStage.TypeMapping);
}
}
さっきは、説明しなかったけどStrategiesのAddメソッドには、Stageと呼ばれるものも一緒に登録する。
これがどういう意味を持つのかわからないので、一応全部登録して動きを見てみることにした。
そして、このExtensionを追加して動きを確認するサンプルを作って動きを見てみた。
public interface IFoo
{
void Foo();
}
public class FooImpl : IFoo
{
public void Foo() { Console.WriteLine("Foo"); }
}
class Program
{
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.AddNewExtension<MyExtension>();
container.RegisterType<IFoo, FooImpl>();
Console.WriteLine("Resolveするよ!!");
var foo = container.Resolve<IFoo>();
foo.Foo();
}
}
これを実行すると、ちょっと長いけど下のような実行結果になった。
Resolveするよ!!
Setup, PreBuildUp
TypeMapping, PreBuildUp
Lifetime, PreBuildUp
PreCreation, PreBuildUp
Creation, PreBuildUp
Initialization, PreBuildUp
PostInitialization, PreBuildUp
PostInitialization, PostBuildUp
Initialization, PostBuildUp
Creation, PostBuildUp
PreCreation, PostBuildUp
Lifetime, PostBuildUp
TypeMapping, PostBuildUp
Setup, PostBuildUp
Foo
Setup, TypeMapping, Lifetime, PreCreation, Creation, Initialization, PostInitializationの順番でPreBuildUpが呼ばれて、今度は逆順にPostBuildUpが呼ばれてるのがわかると思う。
きっと各フェーズで何か処理がされているのだろうけど、ちょっと調べるのは今度にして簡単なサンプルを作ってみた。
サンプルは、IFooインターフェースを実装したクラスを作るときにこっそりインスタンスに細工をするというものにしてみた。
public interface IFoo
{
void Foo();
}
public class FooImpl : IFoo
{
public void Foo() { Console.WriteLine("Foo"); }
}
public class FooDecoratorExtension : UnityContainerExtension
{
protected override void Initialize()
{
// 無条件にIFooをデコレートするStrategyを仕込む
Context.Strategies.Add(
new FooDecoratorStrategy(),
UnityBuildStage.Creation);
}
}
/// <summary>
/// IFooを作成しようとした場合に、無条件にFooDecoratorでデコレートする
/// </summary>
public class FooDecoratorStrategy : IBuilderStrategy
{
#region IBuilderStrategy メンバ
public void PostBuildUp(IBuilderContext context)
{
// IFooならFooDecoratorでくるむ
if (context.Existing is IFoo)
{
context.Existing = new FooDecorator((IFoo)context.Existing);
}
}
public void PostTearDown(IBuilderContext context)
{
}
public void PreBuildUp(IBuilderContext context)
{
}
public void PreTearDown(IBuilderContext context)
{
}
#endregion
}
/// <summary>
/// IFooを飾り付けるよ
/// </summary>
public class FooDecorator : IFoo
{
private IFoo org;
public FooDecorator(IFoo org)
{
this.org = org;
}
public void Foo()
{
Console.WriteLine("デコレート!!");
org.Foo();
Console.WriteLine("デコレート!!");
}
}
class Program
{
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.AddNewExtension<FooDecoratorExtension>();
container.RegisterType<IFoo, FooImpl>();
Console.WriteLine("Resolveするよ!!");
var foo = container.Resolve<IFoo>();
foo.Foo();
}
}
これを実行すると、以下のような結果になる。
Resolveするよ!!
デコレート!!
Foo
デコレート!!
これは、フレームワークとかで特定の型の場合に何か細工入れ込むとかステキな処理が出来そうだ。