пятница, 5 декабря 2008 г.

Much of muchness. Part 5. Handling unhandled exceptions in isolated addin domains, gadget host.


Итак, мы приближаемся к концу серии! Что же нас ждет в этой части?

Судя по списку, мне осталось рассказать о способах борьбы с коррупцией, или лучше сказать с саботажем, в рядах плагинов, о хосте для гаджетов и, оставим на сладкое, кросс-доменный EventAggregator.

Sabotage! Разрабатывая приложение, открытое для расширения при помощи плагинов, вы можете столкнуться с ситуацией, в которой плагины, разработанные сторонними и не очень добросовестными производителями, будут рушить ваше приложение. Вы спросите зачем нам что-то делать, если они и так уже изолированы в sandbox. Но сложность заключается в том, что если в одном из доменов плагинов произойдет необработанное исключение, то кверху лапками полетит все приложение. Решение мое, конечно же основано на рекоммендациях комманды MAF, о том что делать в таких случаях: И состоит это решение из 2ух частей:
  • UnhandledExceptionHelper - вспомогательный класс для обработки исключительных ситуаций, как следует из его названия;
  • GadgetHost - хост-обертка для гаджетов, машина состояний, изменяющая своё представление в зависимости от состояния плагина/гаджета;
UnhandledExceptionHelper
Интерфейс этого незаменимого помощника выглядит следующим образом:



Следуя названию, его призвание обрабатывать исключительные ситуации в плагинах и не давать при этом основному приложению умереть. Как он это делает? Он обманывает CLR, так что его скорее надо было бы назвать UnhandledExceptionLiar =). Обман заключается в перехвате события необработанного исключения из потока в другом домене и блокировка этого потока до завершения приложения. Это единственный способ, который я нашел для безопасного перехвата исключения в домене плагина без последующего закрытия всего приложения. Следует заметить, что если исключение произойдет в UI потоке, даже не смотря на то, что код выполняется в другом домене (ибо потоки умеют пересекать границы доменов), то приложение рухнет. Это может произойти, например, при настройке параметров плагина или в логике gadget'а. Блокировка умирающего потока происходит при помощи метода Lock(), в котором другой поток (как правило UI), захватывает эксклюзивную блокировку. Кто же вызывает этот метод? Неужели это должен делать разработчик? Нет. Об этом и о многом другом позаботится GadgetHost.

GadgetHost.

GadgetHost это контрол, с помощью которого можно управлять состоянием нашего Gadget'a. Он принимает на вход AddInToken, инициаллизирует Gadet в background потоке, а затем, по вызове функции ShowGadget, как следует из названия, создает гаджет и размещает его как свой контент в UI.



При получении AddInToken GadgetHost создает экземпляр UnhandledExceptionHelper'а, который оповещает последнего о поломке и не дает приложению упасть. При поломке надо срочно привлечь внимание пользователя! И с этой функцией справляется мой FaultedGadgetPlaceholder - просто UserControl с эффектной картинкой (полчаса в Paint.Net =) ).

Вкратце так. Не пишите падающие гаджеты! =)

Удачи!

среда, 12 ноября 2008 г.

Much of muchness. Part 4. Publishing MAF addin settings.

Различные виды кризисов задержали продолжение этой серии. Считаю, что надо ускориться и покончить с ней.

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

Итак, может так статься, что вам понадобится управлять какими-то параметрами плагина. Есть несколько способов это сделать: поместить в UI плагина какой-то элемент
, например кнопку, которая будет запускать форму настроек; а можно делать это централизовано, в единой форме настроек хоста, что лично мне кажется более правильным.

Cоздавая плагины на основе MAF, мы имеем возможность хостить их в отдельных доменах приложения и даже в отдельных процессах, а значит, для того чтобы код хоста мог работать с настройками плагинов, их надо подготовить к передаче через эти границы. Так как настроечные интерфейсы представляют из себя совокупность предоставляемой функциональности, то, очевидно, передача по значению в данном случае не подходит, и наши настроечные объекты должны передаваться по ссылке. Эти требования форсировали разработку новых интерфейсов и классов.



Здесь IAddInTuned - интерфейс, который должны реализовывать классы настроек плагинов. Его дополнительная функция, помимо управления настройками, это передача через границу домена несереализуемого объекта изображения WPF, что реализуется в базовом классе AddInTuned при помощи кодирования изображения в массив байтов и последующего восстановления на стороне хоста.



Большинство свойств и функций, реализуемых этим классом интерфейсов, объявлены виртуальными, для того чтобы конкретные наследники класса могли их переопределить. Задачей представления данного объекта на стороне хоста занимается уже встречавшийся нам в одной из первых частей класс AddInTunedHostWrapper. Он не только восстанавливает изображения после передачи через границу домена, но и пользовательский интерфейс, предназначенный для представления и редактирования настроек.



Передается этот объект настроек и пользовательское представление, как вы наверное помните представлению объекта хоста на стороне плагина HostObject.



который затем по цепочке адаптеров передает эти объекты и в адаптере стороны хоста создает обертку AddInTunedHostWrapper для прозрачного представления настроек хосту.



AddInTunedHostWrapper делегирует все входящие вызовы внутреннему переданному TransparentProxy, который инициирует вызов методов объекта AddInTuned в домене плагина через remoting.

Вот так вкратце выглядит решение для передачи настроек плагинов на сторону хоста.

Ну и приведу пример кода, дабы было понятнее.
Сторона плагина.
Сам плагин.

Настройки плагина.


Представление настроек.

Результат.


Надеюсь я вас не разочаровал убогим примером.


Удачи!

пятница, 17 октября 2008 г.

System

J.D. Meyer опубликовал Cheat-Sheet - каталог паттернов и guidance'ов, выпущеных Microsoft P'n'P. Рекомендую добавить в избранное, т.к. это отличная возможность систематизировать знания, тем более, что J.D. уже сделал это для нас! Вот линк: http://blogs.msdn.com/jmeier/archive/2008/10/09/cheat-sheet-patterns-practices-catalog-at-a-glance-posted-to-codeplex.aspx

Удачи!

среда, 24 сентября 2008 г.

Much of muchness. Part 3. WPF-interop and "airspace" notion - hosting gadgets in complex shape windows.

Эта часть будет короткой, и по большей части к разработке самих плагинов не относится. Она, скорее, о WPF-interop, и о том какие проблемы возникают при хостинге gadget'ов (UI плагина) в WPF.

Как я уже говорил в предыдущей части, проблема возникает вследствие того, что представление плагина рендерится не WPF движком (построеном на DirectX), а средствами Win32. Вообще в WPF можно хостить контент отрисованный другими технологиями, но при этом возникает целый ряд проблем, которые в Microsoft очень метко обозвали "airspace". Суть этой проблемы заключается в том, что при хостинге UI отрисованного различными технологиями, эти части UI находятся в изолированных пространствах и подчиняются правилам тех технологий, которые занимаются рендерингом. В нашем случае проблема возникает в случае использования Layered Windows. При попытке захостить gadget в окне с установленным флагом AllowsTranspency и Transparent Background, эта проблема встает в полный рост - WPF владеет всеми пикселами окна и любой другой не-WPF контент будет просто невидим.

Вы спросите зачем нужны layered windows? В WPF это наиболее простой способ создания окон сложной формы - окон без стандартного заголовка (chrome). Но выход есть, и если вы хотите хостить UI плагина в окошках со сложной формой, вам необходимо использовать альтернативный способ создания окна без стандартного заголовка, а именно WinAPI Regions.

Далее привожу код для окна с нестандартным chrome.

Здесь в OnSourceInitialized мы захватываем обект HwndSource, который является (согласно его названию) поставщиком HWND для окна WPF и расширяем стиль окна.

Поскольку наше окно будет лишено стандартного заголовка, то надо как-то его таскать. Об этом заботится обработчик OnMouseLeftButtonDown.

WPF and Win32 Interoperation Overview

  • Part 1. Overview.
  • 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.

Удачи!

среда, 3 сентября 2008 г.

AddIns in Plugins or much of muchness. Part 2 MAF addin UI - gadget, Object model.

//тут введение. можно пошутить, но лучше не стоит.

Пора описать реализацию.

Object model.

Для того, чтобы System.AddIn framework мог обнаружить и хост мог подключить плагины, мы должны должны реализовать требуемый framework'ом pipeline, состоящий и 5 компонент:

  • контракта плагина и, если необходимо, хоста;
  • представления хоста (то как хост видит плагин - изолирует хост и от контракта и от самого плагина подавно);
  • представления плагина (то как плагин видит хост - -//-);
  • адаптеры стороны хоста, транслирующие вызовы представления хоста в вызовы контракта - наследники представления хоста; адаптеры, транслирующие вызовы контракта в вызовы представления хоста - наследники контракта;
  • адаптеры стороны плагина, транслирующие вызовы контракта в вызовы представления плагина - наследники контракта; адаптеры, транслирующие вызовы представления плагина в вызовы контракта - наследники представления хоста.
Теперь по пунктам. Плагины предназначены для публикации в хост гаджетов, соответственно и называются они XXXGadgetAddIn.

Contracts.




IGadgetAddInContract - контракт, с помощью которого хост общается с плагином (функциональность, которую хост требует от плагинов).
IHostContract - контракт, с помощью которого плагин может общаться с хостом (функциональность, которую хост предоставляет плагину).
IAddInTunedContract - вспомогательный контракт для передачи настроек плагина хосту (подробнее о нем я напишу в одной из следующих частей).
IServiceLocatorContract - обертка для передачи набора сервис-локаторов плагину.

Метод Initialize, согласно его имени, предназначен для инициаллизации плагина. В качестве параметра ему передается контракт объекта хоста. Возвращает же этот метод некий id, позволяющий однозначно идентифицировать плагин.



метод Initialize TestGadgetAddIn.

InitializeServices предназначен для инициализации сервисной инфраструктуры плагина, загруженного в другой домен или процесс (я использую ServiceLocator как форму IoC).



метод InitializeServices TestGadgetAddIn.


Эти два метода могут вызываться из фоновых потоков приложения, улучшая тем самым отклик приложения, т.к. процесс инициализации изолированного плагина довольно длительная процедура из-за remoting-вызовов.

Последний метод контракта обязательно должен вызываться из основного потока приложения (UI-поток), т.к. он возвращает представление (gadget) - UI контрол, который будет добавлен в визуальное дерево представления хоста, что само собой влечет за собой требование идентичности Dispatcher'ов контролов.




HostViews.



Представление хоста, как я уже писал изолирует хост от контракта, позволяя изменять версии контракта и соответственно плагина безболезненно для хоста (т.е. он сможет работать даже с более новыми плагинами, реализующими другой, может быть более полный контракт), без перекомпиляции. Для этого лишь понадобится написать новый адаптер. Абстрактный класс представления плагина хостом наследует и реализует адаптер хоста. INativeHandleContract - это обертка, с помощью которой команде WPF удалость передать визуальный объект через границы домена. Реально в визуальном дереве хоста появится не ожидаемый контрол (gadget), который создает плагин, а специальный контрол AddInHost - наследник HwndHost, т.е. контрол позволяющий хостить внутри себя не-WPF контент. Отсюда вывод, что наш gadget будет рендериться не WPF-engine, а Win32. О проблемах, которые это за собой влечет и решении я напишу в одной из следующих частей.

HostAdapters.

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



Адаптер контракта плагина к представлению хоста.



Адаптер хоста к контракту.

Интересный момент здесь, то как передаются настройки - полученный контракт заворачивается в AddInTunedProxy, который восстанавливает UI-специфичные свойства из их сериализованного представления (свойство Image) и публикует настроечный UI при помощи встроенного класса FrameworkElementAdapters, определенного в System.Windows.Presentation.



Вызовы всех метод эта обертка делегирует внутреннему ITunedContract, которые выполняются в домене плагина.

AddInViews.

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




AddInAdapters.



Адаптер плагина к его контракту.
Просто делегирует вызовы контракту.




Адаптер контракта хоста к его представлению.
Наследует абстрактному классу представления хоста плагином. Настройки передаются в виде контракта AddInTunedToContractAdapter, который публикует proxy Tuned для AddInTunedProxy и UI.




Вот в кратце объектная модель решения. В следующей части я расскажу о проблемах, связанных с WPF-interop сценариями, которые возникают при попытке захостить UI плагин, и о решении этих проблем.

Удачи!

четверг, 28 августа 2008 г.

Dependency Injection in xaml

Здесь я опишу свой подход к DI в Xaml. Особенность решения состоит в том, что я использую CAB как платформу для построения моих smartclient приложений, но, надеюсь, за реализацией вы увидите идею, которая позволит вам абстрагироваться от конкретного IoC framework'а.

Итак, довольно часто я создаю объекты в непосредственно в Xaml, и мне бы хотелось при этом не нарушать основные принципы, с которыми я разрабатываю приложение, например, использовать, также как и в коде, IoC-контейнеры для уменьшения связанности системы.



Требования к решению как обычно сводятся к следующим:

  • возможность использовать в Binding;
  • возможность применять к POCO объектам, создаваемым в Xaml.
Решение состоит из двух частей:
  • IoCProvider - custom DataSourceProvider (можно использовать в Binding);
  • набор MarkupExtensions для использования с POCO;
IoCProvider

Этот провайдер - ядро решения. Он занимается выведением зависимостей используя стандартный для CAB IoC контейнер - WorkItem.



Поскольку созданием этого объекта будет управлять WPF engine, то и "впрыснуть" в него зависимости не удастся, но можно использовать доступный отовсюду реестр. В случае WPF это объект Application, а поскольку мой Application обязательно реализует интерфейс IRootApplication, единственное назначение которого публиковать корневой WorkItem, то задача получить IoC контейнер не составляет более труда (см. конструктор IoCProvider).

Основную работу по выведению зависимостей Provider вы полняет в методе BeginQuery, вызываемом WPF binding engine. Инициировать запрос можно также и вручную просто дернув метод Refresh() базового класса DataSourceProvider.



Теперь этот провайдер можно использовать в Binding. Но при попытке привязать поле POCO объекта при помощи Binding к выведенной зависимости, вы получите InvalidOperationException, сообщающее вам о том, что Binding можно использовать только с Dependency Property. И точно, а как же быть?

Markup Extension.

Вот выход - использовать markup extension, ведь его можно использовать практически везде в xaml. Как пример, приведу расширение для "впрыскивания" зависимых сервисов WorkItem - ServiceDependencyMarkupExtension.



И вот результат - мы создаем объект и "впрыскиваем" его зависимости прямо в Xaml.


Надеюсь, теперь вы видите, с какой легкостью можно реализовать Dependency Injection в Xaml, и абстрагировать framework от реализации IoC контейнера. Можно, например, заменить WorkItem на IContainerFacade, как это сделано в Prism.

Вот несколько статей, которые помогут вам в реализации:
creating-a-custom-datasourceprovider
Injecting Xaml with Unity Application Block using Markup Extensions

Удачи!

пятница, 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. Жду предложений по дополнительным фичам!

Удачи!