Страницы с тегами : .NET

Подборка материалов о серверной оптимизации ASP.NET

В процессе подготовки семинара по оптимизации, который пройдет 30 октября 2014 года (регистрация тут - http://gandjustas.timepad.ru/event/150915/), я собрал ранее опубликованные материалы по серверной оптимизации ASP.NET приложений.
Серия постов о кешировании на хабре:
Оптимизация работы с данными:
Какие темы вам еще будут интересны?


Простой способ повысить качество решений

image
Наверное каждый из вас сталкивался с такими решениями для SharePoint: решение вроде работает, но постоянно возникают какие-то проблемы, данные не сохраняются, странные падения при, казалось бы, безобидных операциях. Тестеры тратят много времени на такое решение, но исправление одних багов порождает другие. Развернуть такое решение на production ферме оказывается очень сложно, поддержка превращается в ад. Знакомо, да?

Занимаясь разработкой правил анализа кода для SPCAF (http://spcaf.com), я нашел способ как быстро исправить такую ситуацию.

Найдите все блоки кода такого вида:
try
{
    //код
}
catch(Exception e)
{
    //здесь что угодно, кроме throw;
}

Впишите throw; в блок catch. Важно что в catch указывается базовый Exception, а не конкретный тип исключения.
Ваше приложение начнет падать и вам надо будет исправить все найденные ошибки, не убирая throw. Я такое уже проделывал много раз на разных проектах, и это давало очень положительных результат. Даже если программисты будут сопротивляться, не допускайте убирания throw.

Исследование


Для тестирования правил я собрал около 70 .wsp файлов, решений SharePoint. Большинство из них — проекты, в которых мне довелось участвовать, и я прекрасно знаю все проблемы, которые возникали при разработке. Я посчитал плотность блоков try\catch без throw и вот что получилось:
  • Самое проблемное решение — 1 блок на 36 строк. То есть почти каждый значимый метод был завернут в такойtry\catch.
  • Решения средней паршивости — 1 блок на 90-100 строк. В некоторых из этих решений уже были проведены чисткиtry\catch.
  • Хорошие решения — 1 блок на 120-130 строк. С ними никогда не возникало проблем.

Обоснование


Перехват всех исключений не рекомендуется практически всегда, на него ругается Visual Studio Code Analysis, об этом написано в Framework Design Guidelines. В книге Pragamtic Programmer, которую стоит прочитать всем без исключения программистам, коротко и емко сформулировано правило — Crash, don't Trash.
Фактически перехват всех исключений это попытка подавить ошибку, допущенную программистом. Ошибка все равно вылезает, но не так очевидно и, зачастую, в совершенно другом месте. Это и создает проблемы при разработке решений. Особенно сильно это проявляется для SharePoint, так как его API крайне сложен.
Я знаю только один случай в решениях SharePoint когда оправдано ловить все исключения и не выбрасывать их дальше — в визуальных компонентах, чтобы ошибка разработчика не завалила портал. Но даже в них стоит рассмотреть обработку конкретных типов исключений, вместо перехвата всех подряд. В остальных случаях код должен падать максимально быстро и громко, желательно сообщая о том, что пошло не так. Не надо делать никаких возвратов false или null в случае исключения, пусть код упадет, тогда разработчики и тестеры увидят ошибку, до того как её увидит пользователь.

 

Заключение


Проблема подавления ошибок актуальна не только для SharePoint решений и не только на языке C# и платформе .NET. Возможно и в других проектах вы сможете значительно улучшить ситуацию убирая подавление исключений.
PS. Набор правил, которым проверял решения — http://spcafcontrib.codeplex.com/


Парси строки правильно или как поломали Windows Azure Workflow

Решил я на ночь глядя поиграться в новым Workflow для SharePoint 2013. Для этого надо поставить Workflow Service (WAW).  Приключения мои начались с того что по ссылке инструкция не правильная. По умолчанию на порту 12290 поднимается HTTPS эндпоинт, в а документации указана команда где используется http.

С этим я быстро разобрался, благо не первый день знаком с SharePoint и его особенностями. Но все равно достучаться до сервиса не получается, отдается ошибка.  Event log написано что не удается распарсить значение конфигурационного параметра которое сейчас 0.1.

Покопавшись в конфигах я обнаружил что нужное значение в базе WFResourceManagementDB в таблице WorkflowServiceConfig, там действительно записано “0.1” в поле типа nvarchar(max).

А причина ошибки банальна. У меня русская версия Windows Server и системный разделитель дробной части стоит запятая, а не точка.

Код внутри сервиса парсит строку вызывая Single.Parse(value) и падает на “неверном” разделителе. Из этого мораль: всегда парси (и записывай) конфигурационные параметры с указанием культуры, лучше инвариантной.

Ниже описание ошибки из event log, чтобы этот пост по тексту находился Улыбка

The Workflow Service Backend failed to start at location 'WorkflowServiceBackendHost.OnServiceStarted' due to an exception: System.IO.InvalidDataException: Workflow Service configuration 'WorkflowServiceMaxDispatcherFailureRate' is not formatted correctly. The configuration string should be parsable to a 'Single' type. Current config value is '0.1'.

PS. Использование типа Single намекает на то, что WAW написан на VB.NET.



Блеск и нищета Task Parallel Library

Task Parallel Library (пространство имен System.Threading.Tasks) появилась еще в .NET 4, но большой популярностью не пользовалась. На подходе компилятор C# 5, который поддерживает конструкцию async\await. В скомпилированом коде async метод превращается в метод возвращающий Task\Task<T>.  Поэтому в скором времени использование TPL станет повсеместным.

Task Parallel Library – имеет очень мощные механизмы: вложенные (nested) задачи, локальные очереди для потоков, чтобы уменьшить латентность, work-stealing алгоритмы для работы, кучи опций запуска задач и продолжений, различные планировщики. Все создано в лучших традициях оптимизации асинхронного кода.

Но, TPL создавался до мозга костей практиками и цели сделать библиотеку удобной для потребителей даже не ставилось судя по всему.

  1. Как это не смешно, то в стандартной библиотеке нету метода, позволяющего обернуть значение T в Task<T>. (Только в .NET 4.5 появился Task.FromResult)
  2. Сам объект Task представляет из себя “задачу”, которая может быть как еще не запущена, так и уже выполняться.  Поэтому работа с разными Task_ами будет осуществляться по-разному.
  3. Task не является immutable объектом, метод ContinueWith изменяет саму задачу.
  4. Функция ContinueWith срабатывает при любом завершении задачи, в том числе отмене или ошибке. Необходимо каждый раз указывать флаги.
  5. Исключения внутри задач собираются в один объект, что затрудняет структурированную их обработку.
  6. Метод ContinueWith принимает Func<Task, T>, а не Func<Task, Task>. Это значит что единственный способ связать две задачи – создать вложенную (nested), что может привести к переполнению стека при завершении цепочки вложенных задач.

Последний пункт особенно актуален.  Даже был сделан extension-метод Unwrap, который позволяет писать код в виде:

var task = SomeFuncReturningTask(...)
                .ContinueWith(t => SomeOtherFuncReturningTask(t.Result))
                .Unwrap().ContinueWith(...)
                .Unwrap().ContinueWith(...);


Задачи для проверки навыков разработки SharePoint

Когда я читаю курс 10175, то из положенных 5 дней лекции удается уложить в 4, а в последний день обучающиеся выполняют лабораторную работу, чтобы проверить полученные знания и навыки разработки приложений.

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

  1. Создать систему резервирования ресурсов
    • Создать список ресурсов
    • Создать список (стандартный шаблон списка “Календарь”) со ссылкой на список ресурсов
    • Создать обработчик события добавления и изменения, который проверяет непересекаемость интервалов резервирования для ресурса и отменяет действие в случае пересечения
    • * Создать веб-часть, отображающую свободные  ресурсы в заданном интервале времени
    • Код должен работать в sandbox
  2. Создать соединенные веб-части
    • Создать веб-часть отображающую дерево организаций (подразделений)
    • Сделать её провайдером IWebPartTable
      • При выборе узла дерева веб-часть должна отправлять   профили пользователей в организации
    • Создать веб-часть потребитель IWebPartTable с помощью SPGridView
    • * Реализовать поддержку фильтрации и сортировки в SPGridView как в представлениях SharePoint
  3. Создать рабочий процесс обработки инцидентов
    • Создать список инцидентов (стандартный шаблон списка "”Списко Инцидентов”)
    • Создать State Machine Worflow с 3 состояниями: Открыт, Закрыт, Проверен
    • В каждом состоянии необходимо назначать пользователю (для простоты администратору) задачу, после выполнения задачи удалять её.
    • После после исполнения задачи процесс должен изменять состояние инцидента
    • * Сделать график времени закрытия инцидентов по типам с помощью Performace Point Services
  4. Создать задачу таймера очистки библиотек документов
    • Задача должна находить и удалять пустые папки в библиотеках документов   
    • * Доработать задачу таймера чтобы она работала только на заданных узлах (SPWeb)
    • * Создать Custom Action в Ribbon и SiteMenu для того чтобы вызвать задачу таймера.

ИМХО хороший программист SharePoint должен уметь выполнить любую задачу в течение дня.

Ваши комментарии?



Unity 2.0 Interception

Ранее я писал про механизмы AOP в Unity 1.0 (или 1.2 на тот момент). Недавно увидел этот пост, с примером для Unity 2.0.

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

Оба варианта довольно плохие, первый заставляет править существующий код, второй просто неподъемный. В Unity 2.0 исправили ситуацию с помощью так называемых InterceptionBehavior.

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

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using System.Transactions;


                
            


Не используйте Thread.Start

Большинство программистов .NET начинают свое знакомство с асинхронностью и параллельностью с метода Thread.Start. Это полезно для понимания внутренних механизмов работы многопоточности, но очень вредно для написания production кода.

Вызов Thread.Start чаще всего используется в двух случаях: когда вам надо запустить длительные вычисления параллельно с основным потоком и когда необходимо выполнить некоторый вызов без блокировки основного потока. Эти задачи похожи между собой но эффективно реализуются различными способами.

Откладывание вызова

Для данной операции можно использовать

  1. Пул потоков: System.Threading.ThreadPool - не рекомендуется.
  2. SynchronizationContext и его метод Post
  3. TPL

Выполнение длительной операции

Тут немного другие варианты будут удобны:

  1. BackgroundWorker - практически идеальный вариант для GUI так как содержит события для отслеживания хода выполнения.
  2. TPL при использовании продолжений

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

Как можно понять инструменты вроде TPL позволяют работать на нужном уровне абстракции. TPL может использовать SynchronizationContext, ThreadPool или создавать новые потоки для планирования задач.

UPD. Почему же не следует использовать Thread.Start. Это слишком низкоуровневое средство, создание потоков дорогое, нужно писать код для отмены вычислений, писать код для проброса исключений вызывающему методу, нужно, кроме того большое количество потоков в системе снижает производительность.



Linq и обход дерева

Простой код для префиксного обхода дерева.

public static class Treenumerable
{
    public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> childrenSelector)
    {
        return source.SelectMany(e => Traverse(e, childrenSelector));
    }


                
            


Unity 2.0

Давненько я не писал про Unity. За это время успела выйти новая версия этого IoC-контейнера и появилась новая версия Enterprise Library.

Первое заметное изменение в Unity: теперь требуется подключать одну сборку Microsoft.Practices.Unity. Хотя AOP для Unity все еще лежит в отдельной сборке Microsoft.Practices.Unit.Interception.

Интерфейс класса UnityContainer расширился методами IsRegistered и свойством Registrations. Последнее позволяет анализировать что мы уже положили в контейнер.

Три больших изменения в резолвинге. 

  1. Теперь можно резолвить Func<T>, который будет возвращать зарегистрированный в контейнере экземпляр T. Аналогично можно резолвить Func<IEnumerable<T>>, который будет возвращать все зарегистрированные в контейнере именованные зависимости типа T. Это дает возможность переконфигурировать контейнер во время работы программы, так чтобы остальной код об этом не знал.
  2. При наличии нескольких конструкторов в создаваемом классе по-умолчанию выбирается конструктор с максимальным количеством параметров.
  3. В метод Resolve теперь можно передавать объекты ResolverOverride, позволяющие переопределять поведение Unity при резолвинге.

Добавились два LifetimeManager. HierarchicalLifetimeManager – чтобы дочерние контейнеры резолвили свои экземпляры, а не брали родительский. Это может пригодиться в вебе при создании дочерних контейнеров на каждый запрос. PerResolveLifetimeManager – позволяет переиспользовать один экземпляр в пределах одного вызова метода Resolve.

Подробнее можно почитать в Change Log.

PS. Также есть версия для Silverlight.

PPS. Качать здесь.



Сращивание expression tree

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

from e in context.Entity1Set.Visible()
select e;


IQueryable и Generics

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

При использовании Linq можно написать аналогично

/// <summary>
/// Интерфейс для всех сущностей,
/// которые могут не показываться позьзователю
/// </summary>
public interface IVisible
{
    bool Visible { get; set; }
}


                
            


Аудит изменений с учетом контекста операций

В предыдущем посте я описал способ, который позволяет проводить аудит изменений данных в Entity Framework, не затрагивая сами классы сущностей.

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

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

Для задач логирования было бы удобно иметь доступ к некоторым параметрам контекста, особенно неявным. Эти параметры должны прозрачно передаваться по цепочке вызовов. Для решения таких задач можно применить монады, но тогда придется переписать весь код под использование монад, что очень проблематично. Можно воспользоваться возможностями AOP и IoC чтобы обеспечить неявную передачу явного контекста.

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

public interface IContextManager<T>
{
    void Push(T value);
    void Revert();
    IEnumerable<T> GetValues();
}


Аудит изменений в Entity Framework

В Entity Framework v1 есть возможность подписаться на событие SavingChanges. Это событие вызывается перед записью в базу когда вызывается метод SaveChanges. Чтобы получить все измененные записи в контексте необходимо обратиться к свойству ObjectStateManager контекста и вызвать его метод GetObjectStateEntries.

Ниже приведен код partial класса контекста, который сохраняет изменения сущностей.

 

   1: partial void OnContextCreated()


История одного маппера

Однажды холодным зимним вечером я читал блог Brad Wilson, а именно вот эту статью и понял что нужно писать View-specific models для ASP.NET приложения. Основная проблема с написанием таких моделей заключается в том что приходится писать много “водопроводного кода” для маппинга сущностей базы, возвращаемых ORM на эти самые модели и наоборот. Причем Linq (если поддерживает ORM) позволяет описывать прямое преобразование, но не обратное.

Я начал искать object-to-object мапперы. Сразу нашел AutoMapper, он меня отпугнул монструозностью конфигурации и жутко неудобным (читай статическим) API использования. Кроме того отзывы о скорости работы этого маппера крайне негативные.

Следующим мне на глаза попался EmitMapper. Гораздо более приятный API для использования и довольно высокая скорость работы за счет кодогенерации. Но настройка и кастомизация выполняется очень многословно и непонятно.

В обоих проектах меня не устроил тяжелый API для маппинга. По сути маппинг из типа A в тип B - не более чем функция  A → B, или в нотации типов C# - Func<A,B>.

Я сел писать свой маппер. Естественно для скорости надо заниматься кодогенерацией, но писать свой кодогенератор в несколько килострок кода времени не хватит и проект будет заброшен. Но, к счастью, в .NET начиная с версии 3.5 есть кодогенератор и AST для него. Это классы наследники Expression из пространства имен System.Linq.Expressions, а компилятор тихо сидит в методе Expression<T>.Compile.

Таким образом задача упрощается до безобразия. Необходимо собрать expression tree и скомпилировать его. За два вечера я написал маппер, который поддерживает маппинг массивов, списков, сложных типов, конфигурацию с помощью expression tree и flattering.

Результаты сего труда я залил на Codeplex. Проект назвал Expression Mapper.

Скорость работы мапппера.

Пока писал маппер нашел бенчмарк на хабре. Решил прогнать свой маппер на таком бенчмарке. Результаты немного поразили:

Handwritten Mapper: 88 milliseconds
Emit Mapper: 157 milliseconds
Auto Mapper: 31969 milliseconds
Expression Mapper: 119 milliseconds

Недостатки.

Кроме того что проект еще сырой и требует доработки есть еще один серьезный недостаток. Нету возможности сделать маппер, который не создает новый объект, а изменяет поля в существующем.

Ссылка на страницу проекта - http://expressionmapper.codeplex.com/



Паттерн MVVM. Часть 2.

В первой части я рассказал про байндинги и команды, которые позволяют вынести из формы всю логику во viewmodel.

На просторах интернета можно найти MVVM Toolkit, в котором есть необходимый код, упрощающий разработку приложений с использованием MVVM.

Кроме байндингов и команд немаловажную роль в MVVM играют шаблоны данных (DataTemplate). Они позволяют задавать шаблоны отображения определенных типов, что заметно упрощает композицию элементов UI.

Наиболее подробно, с примерами, применение шаблонов описано в этой статье.



Паттерн MVVM. Часть 1.

MVVM – Model – View – ViewModel – паттерн организации PL (presentation layer – уровень представления).

Паттерн MVVM применяется при создании приложений с помощью WPF и Silverlight.   Этот паттерн был придуман архитектором этих самых WPF и Silverlight - John Gossman (его блог). Паттерн MVVM применяется в Expression Blend.

Идеологически MVVM похож на Presentation Model описанный небезызвестным Фаулером, но MVVM сильно опирается на возможности WPF.

Основная особенность MVVM заключается в том, что все поведение выносится из представления (view) в  модель представления (view model).  Связывание представления и модели представления осуществляется декларативными байндингами в XAML разметке. Это позволяет тестировать все детали интерфейса не используя сложных инструментальных средств.

Я сначала хотел кратко описать применение MVVM и Unity для построения PL, но понял что одного поста для описания возможностей MVVM очень мало.

В WPF для передачи данных между объектами и визуальными элементами используются байндинги (binding – привязка) в простонародии биндинги. Передача может быть как однонаправленная, так и двунаправленная. Работают байндинги с помощью зависимых свойств (DependencyProperty) или интерфейса INotifyPropertyChanged. Передача управляющих воздействий от визуальных элементов осуществляется с помощью команд, реализующих интерфейс ICommand.

Для начала надоевший уже пример SayHello.

Как всегда используется супер-сложный класс бизнес логики:

public interface ISayHelloService
{
    string SayHello(string name);
}
 
public class SayHelloSerivce : ISayHelloService
{
    public string SayHello(string name)
    {
        return "Привет, " + name;
    }
}


Паттерн MVP и Unity

MVP – Model View Presenter – паттерн организации PL (presentation layer – уровень представления).

MVP применяется при создании десктопных интерфейсов. Выделяют три комопнента: есть модель – группа классов, которые отдают данные или получают команды, представление – форма обладающая состоянием и некоторым поведением. Презентер создают для отделения бизнес-логики от деталей GUI-фреймворка. В отличие от MVC в MVP представление определяет презентер, а не наоборот.

MVP обычно строится вокруг существующих GUI-фреймворков. На практике существуют две принципиально различные различные реализации паттерна – Supervising Controller и Passive View.
В первом случае логика помещается в обработчики событий button_click, а сами обработчики помещаются в отдельный класс. Для полной изоляции презентера от деталей представления надо писать достаточно много врапперов\адаптеров.
Во втором случае создается пара интерфейсов для общения между представлением и презентером. При совершении какого-либо действия представление напрямую обращается к презентеру, тот выполняет некоторый код и вызывает установку свойств представления. Passive View способствует максимальному перемещению кода в в презентер, что облегчает тестирование.

Создание MVP в WinForms с помощью Unity.

Создаем новое winforms приложение и грохаем оттуда форму.

Для начала определим служебные интерфейсы.

//Маркерный интерфейс представления
public interface IView
{
}


                
            


Unity + ASP.NET MVC

Недавно на хабре появилась статья о Unity. В одном из комментов предложили связать ASP.NET MVC и Unity.

А вот есть прикольная задачка уже не для начинающих — связать ASP.NET MVC с Unity. Требований в целом три:

— обеспечить связывание инфраструктуры контроллеров MVC с инъектором
— модульность
— уникальность контейнера в разрезе сессии

Я бы расписал все, но блин, не успеваю вообще ничего, кроме работы. А вам в рамках обучающих методик будет полезно, имхо.

хабраюзер acerv

Я напишу серию постов о применении Unity в паттернах уровня представления.

Сначала о том что такое MVC.

MVC – Model View Controller – паттерн организации PL (presentation layer – уровень представления). Целью этого паттерна, как и многих других, служит отделение модели (логики работы программы) от представления (средств отображения информации). В итоге применение такого паттерна должно приводить к улучшению тестируемости кода.

В современном виде MVC применяется в вебе. Выглядит так: есть модель – группа классов, которые отдают данные или получают команды, есть различные представления этих данных – HTML, сделанные с помощью какого-либо шаблонизатора, JSON, SOAP, XML или еще какой – либо. Для того чтобы передавать данные от модели к представления вводят контроллер.

Все MVC фреймворки проектируются так, чтобы управление приходило сразу на контроллер. Контроллер вызывает методы модели, если нужно формирует данные для передачи представлению (эти данные называют Presentation Entity, но термин неустоявшийся), и выбирает представление для этих данных.

ASP.NET MVC – фреймворк для ASP.NET, реализующий паттерн MVC.
Контроллеры в ASP.NET MVC – классы, унаследованные от класса Controller, содержащие несколько методов - “действий”  (actions). По умолчанию действия отображаются на различные urlы см помощью механизма раутинга (System.Web.Routing).
Каждое действие возвращает ActionResult, который может вызывать генерацию HTML, сериализацию данных в JSON итд. Можно писать свои ActionResult.

Применение Unity в ASP.NET MVC.

Инфраструктура ASP.NET MVC получает имя контроллера из параметров запроса, формируемых механизмом раутинга, потом вызывает класс ControllerFactory, который по имени возвращает экземпляр контроллера.

Сначала создадим новое asp.net mvc приложение. Оно уже включает в себя большой функционал, его трогать не будем.

Чтобы подключить Unity к механизму создания контроллеров надо написать свой класс ControllerFactory.  По умолчанию используется DefaultControllerFactory, нам надо изменить в нем один метод, который создает объект контроллера.

public class UnityControllerFactory: DefaultControllerFactory
{
    IUnityContainer _container;


                
            


MEF

MEF – Managed Extensibility Framework – новая библиотека для создания композитных приложений. То есть приложений, которые собираются из отдельных частей.

Скачать его можно по адресу http://mef.codeplex.com/, версия на момент написания поста – Preview 5. На базе MEF построена Visual Studio 2010.

MEF по функциональности похожа на IoC-контейнеры, но авторы не стремились повторить функциональность существующих контейнеров. В MEF есть несколько уникальных фич, которых нету в  IoC-контейнерах.

Сразу примеры, снова поиск фильмов. Возьмем основной код из поста про Unity.

// Фильм
public class Movie
{
    public string Title { get; set; }
    public string Director { get; set; }
    public int Year { get; set; }
}


                
            


Применение Enterprise Library

Все возможности Enterprise Library можно применять по-отдельности и радоваться жизни. Но если бы это была единственная возможность я бы не написал этот пост.

Магия начинается в сборке Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.
В этой сборке в одноименном неймспейсе содержатся call handlerы и атрибуты для навешивания обработчиков на методы. Как использовать call handlerы я рассказывал в этом посте.

Кратко опишу доступные хендлеры:

  • AuthorizationCallHandler – задействует Security Application Block для авторизации при вызове перехваченного метода
  • CachingCallHandler – обработчик, кеширующий результаты вызова перехваченного метода средствами Caching Application Block. Может быть чрезвычайно полезным для веб-разработки.
  • ExceptionCallHandler – позволяет декларативно задавать стратегию обработки исключений в перехваченном методе. Стратегии задаются средствами Exception Handling Application Block.
  • LogCallHandler – задействует Logging Application Block для логгирования вызова перехваченного метода.
  • ValidationCallHandler – вызывает валидацию параметров метода. Параметры валидации должны быть заданы атрибутами или в конфигурации Validation Application Block.
  • PerformanceCounterCallHandler – обработчик, который занимается сбором информации о параметрах быстродействия перехваченного метода. Информация сохраняется в PreformanceCounterах. В самой сборке Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers есть Installer (который можно запустить через installutil), создающий необходимые  счетчики производительности.

Если интересует только runtime AOP, то можно использовать не полновесный IoC-контейнер Unity, а легковесный генератор прокси-классов из Policy Injection Application Block.

Кроме перехватчиков вызовов существует другой способ интеграции всех application blocks с контейнером Uinty. Практический каждый application block из состава enterprise library содержит класс – расширение Unity, которое контейнер возвращать объекты-фасады при запросе определенных типов.

Все расширения можно найти в основной сборке application block, в неймспейсе Configuration.Unity. Классы расширений называются ИмяБлокаExtension.

  • Для Logging Application Block расширение заставляет Unity возвращать фасад логгера по запросу типа LogWriter.
  • Для Exception Handling Application Block  основной тип фасада – ExceptionManager.
  • Для Caching Application Block контейнер возвращает реализации интерфейсов ICacheManager, IStorageEncryptionProvider, IBackingStore.
  • Для Security Application Block контейнер возвращает реализации интерфейсов IAuthorizationProvider, ISecurityCacheProvider.
  • Для Data Access Application Block контейнер возвращает экземпляры класса Database, аналогично DatabaseFactory.CreateDatabase. Можно получать как экземпляр по-умолчанию, так и именованный экземпляр.
  • Для Cryptography Application Block (который находится в сборке Microsoft.Practices.EnterpriseLibrary.Security.Cryptography) расширение заставляет Unity возвращать фасад по запросу типа CryptographyManager, а также реализации интерфейсов IHashProvider и ISymmetricCryptoProvider.

При использовании application blocks совместно с unity все настройки блоков надо задавать в файле конфигурации. Для этого в Enterpise Library есть удобная утилита.

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



Баг в Entity Framework

Обнаружил очень неприятный баг с Entity Framework.
ObjectContext грузит метаданные не при конструировании, а при первом обращении к методам загрузки данных. При этом вызывается this.MetadataWorkspace.LoadAssemblyForType((тип), Assembly.GetCallingAssembly());
Если вызывающая сборка (Assembly.GetCallingAssembly()) не содержит прямой или косвенной ссылки на сборку с метаданными, то загружены они не будут и любой метод чтения отвалится с непонятной ошибкой.

Я наткнулся на такую ситуацию когда вынес extension-метод для ObjecxtContext в отдельную сборку, которая не имела ссылки на сборку с моделью. Сам extension-метод вызывался для только что созданного контекста.

Workaround для этого такой: для только что созданного контекста вызвать context.MetadataWorkspace.LoadFromAssembly(context.GetType().Assembly);



Введение в Enterpise Library

Enterprise Library – библиотека от группы patterns & practicies Microsoft. Проект живет по адресу http://www.codeplex.com/entlib.

Enterprise Library состоит из набора компонент, называемых application blocks, каждый из которых решает определенную задачу, часто возникающую при разработке ПО. 

В состав Enterprise Library входят следующие блоки:

  • Unity Application Block  - IoC-контейнер Unity
  • Policy Injection Application Block – AOP времени выполнения
  • Validation Application Block – небольшой фреймворк для валидации
  • Logging Application Block – логгер
  • Exception Handling Application Block – фреймворк для создания политик обработки исключений в приложении
  • Caching Application Block – фреймворк для кеширования
  • Security Application Block – библиотека для авторизации, практически повторение ASP.NET Membership
  • Data Access Application Block – библиотека, упрощающая работу с ADO.NET, используется другими блоками
  • Cryptography Application Block – библиотека для упрощения работы с криптографическими функциями в .NET

Все эти блоки могут настраиваться через config-файл, для этого в составе Enterprise Library есть утилита упрощающая этот процесс.

Почти все блоки можно использовать по-отдельности, но основная сила Enterprise Library состоит в том, что все блоки можно подключить через Unity.



Фабрики объектов в Unity

В этом посте я описал как с помощью LifetimeManager можно научить Unity использовать фабрику для создания объектов.

На самом деле так делать не стоит. В составе Unity есть сборка Microsoft.Practices.Unity.StaticFactory.dll в которой находится расширение контейнера для использования фабрики объектов.

Регистрация фабрики происходит методом RegisterFactory, который принимает делегат FactoryDelegate. Этот делегат принимает параметром IUnityContainer, и возвращает object.

Пример

var r = new Random();
var container = new UnityContainer();
container
    .AddNewExtension<StaticFactoryExtension>()
    .Configure<StaticFactoryExtension>()
        .RegisterFactory<int>("random", c => r.Next());


AOP времени исполнения в Unity

Для тех кто не знает – AOP это Aspect-Oriented Programming, Аспектно-ориентированное программирование.

При написании любой программы программист производит функциональную декомпозицию, то есть разбивает большие блоки функциональности на более маленькие. Но всегда существуют так называемые cross-cutting concerns  или сквозная функциональность, которая используется всеми остальными частями программы, которую невозможно выделить  в отдельный модуль\класс\метод,
Чаще всего такой функциональностью является логгирование, разграничение доступа, управление транзакциями.

Концепция AOP заключается в том что сквозная функциональность выделяется в отдельные сущности , называемые аспектами, и декларативно задается использование аспектов  в коде.

AOP для .NET может быть реализован двумя способами: изменение кода при компиляции инструментами типа PostSharp или макросами языка Nemerle, или перехват вызовов на стадии выполнения.

В составе Unity есть сборка Microsoft.Practices.Unity.Interception, которая содержит расширение контейнера Unity для перехвата вызовов объектов собираемых контейнером.

Чтобы перехватывать вызовы надо контейнеру сообщить что перехватывать, как перехватывать, и зачем перехватывать.
Что перехватывать задается политиками (Policy), как перехватывать определяют перехватчики (Interceptor), зачем перехватывать определяют обработчики вызовов (CallHandlers).
Эти три части механизма перехвата не зависят друг от друга.

Перехватчики – это классы, реализующие интерфейс IInterceptor. В библиотеке есть классы InterfaceInterceptor для перехвата методов интерфейса, VirtualMethodInterceptor – для перехвата виртуальных методов класса, TransparentProxyInterceptor – для перехвата с помощью прокси-классов, используемых для .NET Remoting.

Обработчики вызовов – это классы, которые реализуют интерфейс ICallHandler, в котором только один нужный метод – Invoke.

Политики бывают двух видов – управляемая атрибутами (AttributeDrivenPolicy) и управляемая правилами (RuleDrivenPolicy).
По-умолчанию используется AttributeDrivenPolicy, которая заключается в том что обработчики вызовов задаются атрибутами, унаследованными от HandlerAttribute, и перехватываются только те методы, для которых заданы эти атрибуты (или атрибуты заданы для классов).
RuleDrivenPolicy позволяет задавать какие методы будут перехватываться с помощью набора правил (IMatchingRule)  и какие обработчики будут при этом вызываться.

Подробнее по этой ссылке http://msdn.microsoft.com/en-us/library/dd140045.aspx

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

Пример

Сначала создадим обработчик, который просто выводит Hello, world на консоль

public sealed class HelloWorldAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new HelloWorldCallHandler();
    }
}


                
            


Что делать если Unity-контейнер надо передавать как зависимость в другие компоненты?

Правильный ответ – ничего.

var container = new UnityContainer();
var resolvedContainer = container.Resolve<IUnityContainer>();


Конфигурация Unity

Существует четыре различных способа конфигурирования контейнера.

1)Использование соглашений. Фактически отсутствие явного конфигурирования. В Unity используется очень простое соглашение, что в классе должен быть один конструктор, который принимает все зависимости параметрами.
Такой способ подходит для 90% случаев если вы пишите код сами.

2)Указание зависимостей с помощью атрибутов. Для свойств есть DependencyAttribute, для конструктора указывается InjectionConstructorAttribute, для метода InjectionMethodAttribute. Для параметров конструктора и injection-методов также можно указывать DependencyAttribute.
При навешивании DependencyAttribute на свойство или параметр можно указать имя зависимости.

3)Задание конфигурации в коде при добавлении элемента в контейнер.
Последним параметром метода RegisterType является массив InjectionMember. В этом массиве можно передать объекты типа InjectionProperty, InjectionConstructor и InjectionMethod чтобы указать с помощью каких членов класса проводить инъекцию.
При указании InjectionConstructor и InjectionMethod для каждого параметра можно указать конкретное значение или попросить контейнер резолвить нужный параметр.
Такой способ дает гораздо более многословен, чем все остальные способы, но дает гораздо большую гибкость. С помощью конфигурации в коде можно использовать IoC с классами, к исходникам которых нет доступа.

4)Задание конфигурации в XML.  Этот способ имеет смысл применять только если нужно изменять конфигурацию без  перекомпиляции  программы. Конфигурация в XML не позволяет описывать произвольные типы, поэтому обладает меньшей мощностью по сравнению с конфигурацией в коде.

Подробнее о возможностях конфигурирования Unity можно почитать по ссылкам
http://msdn.microsoft.com/en-us/library/dd203225.aspx
http://msdn.microsoft.com/en-us/library/dd203208.aspx
http://msdn.microsoft.com/en-us/library/dd203195.aspx
http://msdn.microsoft.com/en-us/library/dd203156.aspx
http://msdn.microsoft.com/en-us/library/dd203230.aspx

Overconfiguration или Темная сторона силы

Часто программисты, познакомившись с IoC-контейнерами и оценив их возможности декларативного конфигурирования, начинают использовать конфигурацию по любому поводу. Это приводит к тому что конфиги становятся монструозных размеров и абсолютно нечитаемые. При этом по коду программы абсолютно невозможно понять какой код когда выполняется. Любые изменения затрагивают не только код, но и конфигурацию, что может привести к проблемам при развертывании.

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



Управление временем жизни объектов в Unity

При регистрации класса или объекта в контейнере можно указать объект LifetimeManager, который будет управлять временем жизни экземпляров в контейнере.

Класс LifetimeManager очень простой, в нем всего три метода GetValue, SetValue и RemoveValue, причем последний не используется.
При помещении объекта в контейнер вызывается метод SetValue, а при необходимости получить объект вызывается GetValue и если он вернул null, то создается новый объект.

В библиотеку Microsoft.Practices.Unity входит несколько менеджеров.

TransientLifetimeManager – ничего не сохраняет, GetValue всегда возвращает null, поэтому объект создается каждый раз. Этот менеджер используется по-умолчанию при вызове RegisterType.

ContainerControlledLifetimeManager – сохраняет объект в локальной переменной. Поэтому объект живет столько же, сколько и контейнер. Этот (вернее другой, но с таким же поведением) менеджер используется по-умолчанию при вызове RegisterInstance.

ExternallyControlledLifetimeManager – сохраняет слабую ссылку (WeakReference) на объект. При использовании этого менеджера и вызове RegisterInstance сам вызывающий код должен управлять временем жизни объекта, помещенного в контейнер. Когда используется RegisterType этот менеджер будет выдавать уже существующий экземпляр объекта если он есть.

PerThreadLifetimeManager – сохраняет объекты в ThreadStatic словаре. Таким образом каждый поток в программе будет использовать свой набор объектов.

Для применения Unity в ASP.NET приложениях очень легко реализовать LifetimeManager, который сохраняет объект в контексте или в сессии.

Другие области применения LifetimeManager

В Unity нет возможности регистрации метода создания объектов, но это очень легко исправить с помощью своего LifetimeManager и пары extension-методов.

public class FactoryLifetimeManager<T>: LifetimeManager
{
    Func<T> _factoryMethod;
    LifetimeManager _baseManager;


                
            


Инъекция массивов в Unity

Unity умеет резолвить зависимости-массивы. Для такой зависимости контейнер возвращает объекты всех подходящих типов. Даже при разрешении зависимостей с указанием имени будут возвращены все подходящие типы.

Также этого Unity поддерживает разрешение обобщенных массивов.

Пример

Интерфейс и классы логгеров

public interface ILogger
{
    void Write(string message);
}


                
            


Использование generic-ов в Unity

Unity поддерживает работу с generic-типами. 
Можно в контейнер поместить обобщенный тип,  а потом запросить тип с конкретными параметрами.  Подробнее описано по ссылке http://msdn.microsoft.com/en-us/library/dd203156.aspx

Пример кода, использующего такую фичу.

Рассмотрим обобщенный класс репозитария для работы с данными

public interface IRepository<T>
{
    IQueryable<T> Items();
    void Save(T item);
    void Delete(T item);
}


Рефакторинг legacy кода для использования Unity

Представим себе код, который был написан без применения принципов IoC.
Например такой

public class AccountManager
{
    public void CreateAccount(string userName, string password)
    {
        var accountStore = new AccountStore();
        accountStore.AddNewAccount(userName, password);
    }


                
            


Используем IoC-контейнер Unity

Unity – IoC-контейнер от Microsoft, разработанный группой Patterns&Practicies. Найти его можно по адресу http://www.codeplex.com/unity. Также Unity включен в состав Enterprise Library.

Будет использоваться версия 1.2, последняя на данный момент.

Итак приступим.
Как завещал дядька Фаулер будем рассматривать использования контейнера на примере программы, работающей  с базой данных фильмов.

Чтобы использовать Unity в своей программе надо подключить сборки Microsoft.Practices.ObjectBuilder2 и Microsoft.Practices.Unity.

Нам надо написать класс, который позволяет искать фильмы по различным параметрам.

Предположим что фильмы описываются классом Movie

public class Movie
{
    public string Title { get; set; }
    public string Director { get; set; }
    public int Year { get; set; }
}


Введение в IoC

(Пост на RSDN)

Есть класс A и зависит от от класса B (использует его).

Например так:
public class A
{
   B b = new B();
   void foo()
   { 
      //используем b
   }
}