かずきのBlog@hatena

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

Unityで属性ベースでの構成

Enterprise Library 5.0のデフォルトのDIコンテナのUnityが便利そうではあるんだけどMEFみたいに属性でコンテナに登録ができないっぽい。ということで、こんなのでっちあげてみた。

自動登録したいクラスにこの属性をつけると登録されるみたいな感じ。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class AutoRegistAttribute : Attribute
{
    private static readonly InjectionMember[] InjectionMembersInitialValue = new InjectionMember[0];

    public AutoRegistAttribute()
    {
        this.InjectionMembers = InjectionMembersInitialValue;
    }

    public string Name { get; set; }
    public Type RegisterType { get; set; }
    public InjectionMember[] InjectionMembers { get; set; }
}

んで、この属性がついてるものをコンテナに登録する拡張。

public class AutoRegisterExtension : UnityContainerExtension
{
    private Assembly[] loadTargets;

    public AutoRegisterExtension(params Assembly[] loadTargets)
    {
        this.loadTargets = loadTargets;
    }

    protected override void Initialize()
    {
        var targetTypes = loadTargets.SelectMany(assembly => assembly.GetTypes())
            .Where(type => type.GetCustomAttributes(typeof(AutoRegistAttribute), false).Any())
            .Select(type => new
            {
                Type = type,
                Attribute = type.GetCustomAttributes(typeof(AutoRegistAttribute), false).OfType<AutoRegistAttribute>().First()
            });

        foreach (var type in targetTypes)
        {
            var registerType = type.Attribute.RegisterType ?? type.Type;
            if (string.IsNullOrEmpty(type.Attribute.Name))
            {
                this.Container.RegisterType(registerType, type.Type, type.Attribute.InjectionMembers);
            }
            else
            {
                this.Container.RegisterType(registerType, type.Type, type.Attribute.Name, type.Attribute.InjectionMembers);
            }
        }
    }
}

使ってみよう

使い方は割と簡単。登録したいクラスに属性をつけるだけ。

public interface IGreeter
{
    void Greet();
}

[AutoRegist(RegisterType = typeof(IGreeter))]
public class Greeter : IGreeter
{    
    public void  Greet()
    {
        Console.WriteLine("Hello world");
    }
}

[AutoRegist]
public class Client
{
    [Dependency]
    public IGreeter Greeter { get; set; }

    public void Execute()
    {
        this.Greeter.Greet();
    }
}

そして、Mainメソッドの中でこんな感じに使う。

var container = new UnityContainer()
    .AddExtension(new AutoRegisterExtension(Assembly.GetExecutingAssembly()));
var client = container.Resolve<Client>();
client.Execute();

これで、少しは楽が出来るかな。というか、何でこういうのが無いんだろうか?Unityのポリシー?