пятница, 22 августа 2008 г.

Transient Objects Injection in CAB

Если вы испытывали затруднение с тем, как внедрять не singleton-зависимости в CAB, то вот вам решение:

  • TransientContainerService;
  • TransientDependencyStrategy;
  • TransientDependencyAttribute;
Дело в том, что в IoC-контейнере CAB зарегистрировать тип/объект можно только как singleton, но иногда такое поведение не устраивает, а простых средств это ограничение обойти я не нашел.

Зато такие возможности есть в любом полноценном IoC-контейнере, например в Unity, который я и использовал. В результате мы имеем 2 IoC-контейнера в одном приложении!

Теперь по порядку.

TransientContainerService

Как я уже говорил, я использовал в своем решении Unity, но дабы не вводить жесткую зависимость от одной реализации контейнера я ввел интерфейс ITransientContainerService, который абстрагирует нас от конкретной реализации.

TransientContainerService наследник интерфейса, использующий Unity поумолчанию.


TransientDependencyStrategy

Для того, чтобы вся конструкция заработала необходимо добавить сию стратегию в коллекцию стратегий ObjectBuilder'а на этап инициаллизации объекта (т.к. объект к этому моменту уже должен существовать). Ее задача находить "временные" (transient) зависимости и вычислять их.

TransientDependencyAttribute

Для того, чтобы стратегия поняла что именно необходимо внедрить, зависимость необходимо пометить атрибутом TransientDependencyAttribute. У этого аттрибута есть строковое свойство Id, с помощью которого можно вычислить именованную зависимость.
Основная работа по выведению зависимости происходит в методе GetValue внутреннего класса TransientParameter. Зависимость выводится при помощи вышеописанного сервиса ITransientContainerService, простой делегацией этого процесса Unity. Далее, созданный объект пропускается через конвеер стратегий ObjectBuilder'а, с тем чтобы внедрить в него зависимости, т.к. он тоже может иметь зависимости в свойствах и методах. Единственное ограничение здесь будет необходимость в конструкторе указывать тоько временные зависимости, т.к. создавать этот объект будет не ObjectBuilder CAB'а, а Unity.


Пример.

Так может выглядеть это в коде:



Удачи!

P.S. Ниже привожу список ресурсов и статей, полезных для понимания ObjectBuilder, Unity и IoC вообще.
http://tavaresstudios.com/Blog/post/Deconstructing-ObjectBuilder---Introduction.aspx

http://tavaresstudios.com/Blog/post/Deconstructing-ObjectBuilder---What-Is-ObjectBuilder.aspx

http://tavaresstudios.com/Blog/post/Deconstructing-ObjectBuilder---Combining-Strategies.aspx

http://tavaresstudios.com/Blog/post/Deconstruction-ObjectBuilder---Wiring2c-part-1.aspx

http://tavaresstudios.com/Blog/post/End-of-the-Deconstruction.aspx

http://davidhayden.com/blog/dave/archive/2008/02/27/UnityConstructorInjectionGreediestMostParametersTheLineHasBlurredMaybe.aspx

http://weblogs.asp.net/podwysocki/archive/2008/03/25/ioc-and-unity-the-basics-and-interception.aspx

http://weblogs.asp.net/podwysocki/archive/2008/02/22/ioc-and-the-unity-application-block-going-deeper.aspx

http://weblogs.asp.net/podwysocki/archive/2008/02/26/ioc-and-the-unity-application-block-once-again.aspx

http://weblogs.asp.net/podwysocki/archive/2008/03/04/ioc-containers-unity-and-objectbuilder2-the-saga-continues.aspx

http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx

пятница, 1 августа 2008 г.

Kodama - a spirit of tree.

Те из вас, кто читает мою англоязычную версию блога, возможно, читали мою заметку про то как ViewModel спасает людей (вообще говоря ViewModel капризничает и спасает только WPF developer'ов ). Мне было недосуг переводить ее, но вот вкратце ее содержание: хотите облегчить себе жизнь - используйте ViewModel где только возможно. В данном случае я использовал ViewModel при работе с ComboBox. Что из этого вышло? Вышел замечательный и удобный класс SelectorCollectionPresenter, жить с которым намного легче. =) В конце я в качестве бонуса опубликую его код. А сейчас я хочу написать не об этом.

Хочу написать я о ViewModel, взаимодействующем с TreeView. Вдохновение меня посетило когда я прочитал вот эту статью Josh'а Smith'а. C тех пор прошло уже много времени и я почти не работал с WPF непосредственно, а больше решал инфраструктурные проблемы (я начал серию заметок про масло масляное), но идея мне понравилась и засела в голове.

И вот, наконец, я обобщил и собрал воедино свое видение этой проблемы. В результате был написан контрол имитирующий TreeView "с душой". Назвал я его KodamaView. Kodama в японской мифологии - это дух живущий в дереве. А Kodama моего дерева, как вы наверное уже догадались это ViewModel.

- он сказал ViewModel?

Хочу сразу предупредить, что я просто собрал воедино в повторно используемый компонент свои наработки и идеи Josh'а.

KodamaView

KodamaView - это UserControl, который хостит внутри себя TreeView. Дабы сымитировать поведение TreeView он реализует интерфейс ITreeView, унаследованный от моего
базового интерфейса IWPFView путем делегирования всех вызовов внутреннему TreeView.


интерфейс для имитации TreeView.


Собственно сам контрол.


Стили и темплейты.

Kodama.

И теперь о самом главном - о душе.


Вот она душа дерева на картине
Toriyama Sekien

Моя кодама (если можно так выразиться) - это наследник класса ViewModel, параметризованного интерфейсами ITreeView и IDataModelBase. Последний объявлен базовым для того, чтобы ему подсунуть любую DataModel. Обновив ее, наследник Kodama получит список или иерархию объектов, которые отрендерит TreeView.

Для каждого TreeViewItem'а создается своя Kodama - TreeViewItemViewModel.



В данном классе предусмотрена возможность "ленивой" загрузки дочерних элементов при раскрытии узла (метод LoadChildren должен быть переопределен в наследниках) (честно сперто у Josh'а).



И наконец, я написал generic-класс TreeViewItemViewModel`1, единственное назначение которого - публиковать сущность для привязки в шаблоне.



В качестве примера - Kodama дерева департаментов.




А вот и бонус - SelectorCollectionPresenter. За всеми подробностями сюда.



Удачи!

четверг, 17 июля 2008 г.

AddIns in Plugins or much of muchness. Part 1. Overview.

Совершенно верно, вы не ошиблись, и это не двоит у вас в глазах. Именно маслом маслянным я реализовал подключение стронних плагинов в композитном клиенте, построенном на SCSF (Smart Client Software Factory).

Существует много терминов для обозначения одного и того же паттерна проектирования - подключаемого модуля (plugin, addin, addon и т.п.). Ребята из P'n'P назвали подключаемые модули в CAB -plugins, а парни из Managed AddIn Framework (MAF) - addins, соответственно. Сразу возникает вопрос - а зачем вообще мешать все в кучу, если в CAB и так уже plugin является краеугольным камнем? Ответ прост - в MAF заложено множество возможностей по работе с плагинами (так и буду дальше их называть), которых нет в CAB. Например: возможность изоляции плагинов в отдельных доменах или даже процессах, возможность запуска плагинов с различными уровнями безопасности и др.

Хороший пример работы с MAF написал Sacha Barber здесь. А вообще, очень много полезной информации можно почерпнуть в блоге комманды MAF.

Дизайн системы следующий: в Shell - оболочку программы, загружаются модули CAB, один из которых управляет подключением MAF плагинов. Схематично это можно представить себе
примерно так:


Модуль App.Addins публикует в настроечную инфраструктуру UI для управления состоянием MAF плагинов. Этот модуль управляет временем жизни и окружением MAF плагинов, позволяя вам подключать или отключать те или иные плагины, выбирать с каким уровнем изоляции и в какой домен/процесс загружать выбранный плагин.

Жизнь становится интереснее с плагинами, имеющими свой собственный UI, а также публикующими настройки! Для таких плагинов модуль App.Addin позволяет выбрать в какую часть UI приложения-хоста загрузить gadget (так я называю UI MAF плагина).

Возможным расположением gadget'ов заведует модуль App.Layout, загружающий layout из loose-xaml, который можно редактировать в любом xaml-дизайнере.


Вот, в общих чертах, реализация масла маслянного. В следующих частях постараюсь рассмотреть конкретные проблемы, которые могут быть вам интересны, и их решения.

Part 2. MAF addin UI - gadget. Object model.

Part 3. WPF-interop and "airspace" notion - hosting gadgets in complex shape windows.
Part 4. Publishing MAF addin settings.
Part 5. Handling unhandled exceptions in isolated addin domains, gadget host.
Part 6. Cross-AppDomain events propagation powered by Juval Lowy's WCF Pub-Sub framework.

Удачи!

вторник, 24 июня 2008 г.

WPF Presentation layer

Я не буду блоггерствовать следующие 3 недели, но чтобы вы не расстраивались вот вам мой Presentation Layer.

В этом проекте реализован небольшой framework для разработки десктопных приложений на WPF с примерами, которые я уже выкладывал в предыдущих постах. В основном это реализация шаблона DM-V-VM.

В качестве бонуса я включил исходные коды Wizard контрола.

Наслаждайтесь.

среда, 11 июня 2008 г.

Enterprise logging system. Part 5. Features.

Как я уже говорил, в LogBook предусмотрены дополнительные фичи, позволяющие использовать его в самых различных сценариях. Например:

Сцена 1. Сервис. Действующие лица: WCF, LogBook.

Поскольку framework WCF черезвычайно легко расширяется, подключить LogBook к нему одно удовольствие. На тему расширения WCF очень хорошо написал Aaron Skonnard здесь. Так вот, LogBook расширяет WCF при помощи поведения сервиса, реализованного в виде аттрибута, который вы можете декларативно применить к любому WCF сервису. Что же добавляет это поведение? Оно добавляет ErrorHandler ко всем ChannelDispatcher'ам и MessageInspector ко всем DispatchRuntime'ам EndpointDispatcher'ов.

Таким образом соотвественно настроенный LogBook может логировать ошибки и трассировать сообщения, происходящие при работе сервиса.


ErrorHandler, вызывает метод WriteErrorMessage фасада LogBook.


MessageInspector, вызывает метод WriteTraceMessage фасада LogBook.

Сцена 2. DLinq. Действующие лица: DLinq, LogBook.
В DLinq предусмотрено подключение простого логгера, в который будут сваливаться все запросы, выполняемые системой. Этот логгер должен быть наследником TextWriter и переопределять как минимум метод Write(string). Для LogBook я написал простенькую реализацию, которая будучи подключенной к любому DLinq DataContext'у будет трассировать все запросы.


Linq tracer.

Сцена 3. .Net. действующие лица: System.Diagnostics, LogBook.
И последнее. Вы можете подключить LogBook к системе трассировки .Net при помощи простого TraceListner. Довольно удобно для приложений, код которых вам недоступен. С помощью этого прослушивателя трейсов вы сможете перенаправить сообщения в сервис логирования.



TraceListener.

Кроме того, в System.Diagnostics.Trace нельзя было передать контекст. А здесь можно.

Вот вкратце все сладости в LogBook. Жду предложений по дополнительным фичам!

Удачи!

вторник, 10 июня 2008 г.

Enterprise logging system. Part 4. Facade.

Ура, я проснулся от летаргического сна.

Предлагаю сразу взяться, например за дело.

Итак вперед.

В первой части я кратко описал требования к системе и архитектуру. В этой части я расскажу как эти требования были реализованы.

Самый очевидный способ интегрировать систему в свое приложение (о неочевидных я поговорю в следующий раз) это императивное использование ее в коде. Для данного сценария в системе предусмотрен простой фасад, которой за своей простотой скрывает тот объем работы, который необходимо проделать, чтобы гарантированно доставить сообщения в хранилище и удовлетворить всем предъявленным к ней (системе) требованиям.

Как вы помните в системе предусмотрено 5 уровней логирования:

  • Trace;
  • Info;
  • Warn;
  • Error;
  • Status;
, соответственно и интерфейс фасада определен количеством и именами этих уровней.

Что же скрывают за собой эти методы? Давайте рассмотрим работу LogBook по шагам.

При первом обращении к фасаду, прежде чем выполнить метод, clr будет вызван инициализатор типа. В этом статическом конструкторе LogBook пытается сконифигурировать себя. Этот процесс он делегирует статическому классу-загрузчику Bootstrapper, который сначала пытается получить настройки для текущего клиента от сервиса, а затем производит инициаллизацию хозяина (LogBook) из локального конфига (если вдруг в момент старта сервис будет недоступен, то он сможет поднять LogBook по локальным настройкам, которые сохраняются в конфигурационном файле клиента, а если не сможет, то кинет исключение).

Далее, в зависимости от уровня логирования LogBook создаст соответствующее сообщение и отправит его на валидацию в список подключенных фильтров (важно сделать это до окончательного заполнения сообщения, чтобы не тратить память и время зря, если оно не проходит по условиям фильтров). После этого, дабы не блокировать клиента, фасад LogBook делегирует отправку сообщения RequestManager'у, который в асинхронном режиме постарается отправить его куда следует. Куда следует - это сюда: первым делом он сохранит его в локальной стратегии, а затем попытается отправить его сервису. В случае неудачной попытки, он увеличит счетчик неудачных попыток, проверит условия устаревания сообщения (по времени хранения и количеству неудачных попыток) и сохранит обратно сообщение в локальную очередь, если оно все еще валидно. За подключением сервиса следит ConnectionMonitor, который при изменении статуса подключения генерирует событие, перехватываемое RequestManager'ом.

Все методы перегружены одним дополнительным параметром - контекстом, о котором я писал здесь. Если вызывается метод без котекста, или вместо него передается null, то в качестве контекста будет автоматически подставлен LogContext.Current.

Вот в очень сжатом виде механизм работы моего enterprise LogBook'а.

Подумываю завести проект на CodePlex. Займусь этим на досуге.

Удачи!

вторник, 3 июня 2008 г.

Handy Monitor wrapper.

Вам никогда не требовалось узнать, стоят ли за текущим потоком в очереди другие потоки на блокировке? Если да, то вы наверняка уже полазили по классу Monitor в поисках свойства, которое бы называлось как-нибудь вроде LockCount или Busy. И тогда-то вы уж точно знаете, что это "конфиденциальная информация"! =)

Ниже я привожу простой и удобный класс-обертку над Monitor, который предоставляет такую информацию совершенно бесплатно. И что самое прекрасное, блокировка также осуществляется с поддержкой языка (я имею ввиду синтаксические конструкции, которые позволяют писать элегантный код). Но есть небольшая разница, если блокировка Monitor осуществляется конструкцией lock(object) {}, то в данной реализации она осуществляется конструкцией using(object){}. Ниже код класса и пример.





Вот пример использования:



Удачи!

P.S. Реализацию я подглядел в недрах какой-то из системных сборок.