Столько шума и публикаций вокруг Unity не оставили меня равнодушным. Мне лично захотелось поближе познакомиться с Unity, когда я узнал что в Unity 1.2 появился Per Thread Storage Lifetime Manager. Подумать только! Для каждого потока возвращается отдельный экземпляр в методе Resolve. Не так давно коллега прислал ссылку на Unity без коментариев – дескать в продукте надобы использовать его вместо Dictionary<Type, object>, который выполняет роль контейнера зарегистрированных сервисов. Пришлось познакомиться с Unity поближе. И мое терпение лопнуло. Из моего поста вы узнаете, что Unity – это плохой, неудачный дизайн, его сложно использовать в реальных ситуациях, и сам Application Block “плохо пахнет”.
Для начала – почему Unity это over-design. Ответ - анонимный делегат. Да! Проще всего сконфигурировать контейнер и инвертировать зависимости с помощью анониного делегата в методе Register. Сразу отпадает необходимость в развесистой и неудобной иаерархии с красивым названием “Lifetime Managers”. А вместе с ней "в топку" отправим и другую избушку на курьих ножках (e.g. кривая, неудобная реализация) вокруг концепции "Injection of Contructor, Properties и Initializers".
Transient Lifetime Manager
ConfigurationContainer container = new ConfigurationContainer();
container.Register<ISampleInterface>(
delegate
{
return new SampleClass();
});
Singleton (aka RegisterInstance)
SampleClass instance = new SampleClass();
ConfigurationContainer container = new ConfigurationContainer();
container.Register<ISampleInterface>(
delegate
{
return instance;
});
Injection of Contructors, Properties and Initialisers
ConfigurationContainer container = new ConfigurationContainer();
container.Register<ISampleInterface>(delegate
{SampleClass ret = new SampleClass(12, "Bye-bye, Unity!");ret.MyProperty = new object();ret.MyStringProperty = "Some Text";
ret.InitializeMe(42.0m, container.Resolve<ILogger>());return ret;
});
Для всех кто не читает справку приведу пример из справки Unity. Почувствуйте разницу:
IUnityContainer myContainer = new UnityContainer();
myContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyObject>(
new InjectionConstructor(12, "Hello Unity!"),
new InjectionProperty("MyProperty"),
new InjectionProperty("MyStringProperty", "SomeText"),
new InjectionMethod("InitializeMe", 42.0,
new ResolvedParameter(typeof(ILogger), "SpecialLogger"))
);
PerThreadLifetimeManager
ConfigurationContainer container = new ConfigurationContainer();
container.Register<ISampleInterface>(delegate
{const string key = "Sample Interface Implementation";LocalDataStoreSlot slot = Thread.GetNamedDataSlot(key);SampleClass ret = (SampleClass)Thread.GetData(slot);if (ret == null){ret = new SampleClass();
Thread.SetData(slot, ret);}return ret;
});
TimeLimitedLifetimeManager
И в заключении – немного о том почему Unity сложен в использовании. Здесь я приведу очень древний пример реализации конецепции Lifetime Manager. Реализация древняя настолько, насколько древний сам .Net. Я пользуюсь реализацией ниже со времен появления .Net в 2002-м году. Для тех, кто не знает, напомню – Unity вышел в 2008 году. Сама концепция описывается одним простым предложением – Объекты “живут” в контейнере строго ограниченное время. Т.е. по истечении определенного времени объект из контейнера освобождается. Итак – сама реализация:
ConfigurationContainer container = new ConfigurationContainer();
container.Register<ISampleInterface>(delegate
{const string key = "Sample Interface Implementation";SampleClass ret = (SampleClass)HttpRuntime.Cache.Get(key);if (ret == null){ret = new SampleClass();
HttpRuntime.Cache.Insert(key,ret,null,
DateTime.Now.AddMinutes(60),Cache.NoSlidingExpiration);}return ret;
});
Дополнение: Lazy Singleton Lifetime Manager
Золотой ключ подхода - отложенное инстанцирование синглтона до вызова метода Resolve. С помощью ключика решается проблема очень сложных зависимостей между экземплярами контейнера
ConfigurationContainer container = new ConfigurationContainer();
container.Register<ISampleInterface>(delegate
{return Lazy.Get(
"Sample Interface Implementation",
() => new SampleClass(container.Resolve<IFoo>(),"2"););});