Как подружить ASP.NET Controls и DI-контейнер
Интро
В последнее время решил немного освежить свои знания в ASP.NET, в связи с чем углубился в процессы генерации кода контролов по разметке (*.ascx, *.aspx) и обнаружил что можно делать очень интересные решения, о которых о хочу поведать. Итак сегодня мы узнаем, как подружить наш Dependency Injection контейнер с генерируемым контролами кодом.
Поехали
В качестве DI-контейнера будет выступать Microsoft Unity, но это не принципиально, всё что будет касаться DI не зависит от используемого контейнера.
Проблема состоит в следующем – есть некоторый ASP.NET Control, в который мы хотим внедрит зависимости, а так же воспользоваться услугами Service Locator’а для управления интересующими нас зависимостями.
В Microsoft Unity есть некоторые средства для того, чтобы сделать это не прилагая особенных усилий: мы можем произвести инъекцию в свойство элемента управления, нас интересующее примерно следующим образом:
- Отметить атрибутом Dependency необходимое свойство
public class MyControl : UserControl
{
[Dependency]
public MyPresenter Presenter
{
get { return _presenter; }
set
{
_presenter = value;
_presenter.View = this;
}
}
} -
Проинициализировать элемент управления можно следующим образом
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
_сontainer.BuildUp(GetType(), this);
} -
Позаботиться о местоположении контейнера в вашем приложении, я предлагаю использовать для этого HttpApplication, унаследовавшись от которого и произведя небольшие модификации файла global.asax мы получаем необходимое нам хранилище для контейнера, обращаться с ним необходимо примерно следующим образом
((Sapphire.Application)HttpContext.Current.ApplicationInstance).Container
Решение вполне пригодное, однако пуристические воззрения не дают оставить решение на данной стадии, и думаю, что просто необходимо заменить инъекцию свойства на инъекцию в конструктор, тем более подобный подход – это далеко не то, что мы можем выжать из Unity.
Т.е. наш интерес состоит в том, чтобы класс MyUserControl выглядел примерно так (думаю сборщику страницы это не совсем понравится)
{
public MyControl(MyPresenter presenter)
{
_presenter = presenter;
_presenter.View = this;
}
}
Предлагаю этим и заняться. Начнём с того, что у элементов управления, описанных в разметке страницы, при генерации страницы указываются их конструкторы без параметров, интересно, как можно управлять данным процессом, первоначально, покопавшись в web.config я предполагал сделать это через:
<add extension=«.aspx» type=«System.Web.Compilation.PageBuildProvider»/>
<add extension=«.ascx» type=«System.Web.Compilation.UserControlBuildProvider»/>
…
</buildProviders>
Однако реализация своего PageBuildProvider’а – довольно серьезное занятие, думаю отложить это для серьезной на то необходимости. Однако благодаря BuildProvider’ам можно генерить к примеру слой доступа к данным, для этого надо:
Написать и зарегестрировать обработчик для какого-нибудь своего расширения, к примеру *.dal и сделать что-нибудь наподобее http://www.codeproject.com/KB/aspnet/DALComp.aspx
кстати подобная логика реализована в SubSonic http://dotnetslackers.com/articles/aspnet/IntroductionToSubSonic.aspx
так же интересная реализация наследования страницы от generic типов http://stackoverflow.com/questions/1480373/generic-inhertied-viewpage-and-new-property
ещё можно, к примеру генерировать исключения, объекты передачи данных и многое другое, ограничением является лишь ваша фантазия.
Вообщем, данный вариант нам не подходит, необходимо сделать что-нибудь проще, и есть отличное решение, с помощью атрибута ControlBuilder мы можем указать свою логику сборки элемента управления из разметки, это будет выглядеть примерно так
public class UserControl : System.Web.UI.UserControl
{
}
Теперь разберемся с реализацией MyControlBuilder, этот тип должен наследовать от ControlBuilder и с помощью перегрузки ProcessGeneratedCode мы с вами сможем указать сборщику на необходимость использования нашего кода вместо вызова конструктора без атрибутов элемента управления:
CodeTypeDeclaration baseType,
CodeTypeDeclaration derivedType,
CodeMemberMethod buildMethod,
CodeMemberMethod dataBindingMethod)
{
codeCompileUnit.Namespaces[0].Imports.Add(new CodeNamespaceImport(«Sapphire.Web.UI»));
ReplaceConstructorWithContainerResolveMethod(buildMethod);
base.ProcessGeneratedCode(codeCompileUnit, baseType, derivedType, buildMethod, dataBindingMethod);
}
самое интересно скрывает метод ReplaceConstructorWithContainerResolveMethod
{
foreach (CodeStatement statement in buildMethod.Statements)
{
var assign = statement as CodeAssignStatement;
if (null != assign)
{
var constructor = assign.Right as CodeObjectCreateExpression;
if (null != constructor)
{
assign.Right =
new CodeSnippetExpression(
string.Format(«SapphireControlBuilder.Build<{0}>()»,
ControlType.FullName));
break;
}
}
}
}
следуя по коду можно обратить внимание, что он заменяет вызов конструктора на вызов генерик-метода Build, в котором мы и обратимся к нашему контейнеру с просьбой вызвать наш элемент управления и проинициализировать его конструктор необходимыми зависимостями.
Однако это ещё не решении задания, т.к. есть метод динамической загрузки элемента управления Page.LoadControl(), для него придётся написать свой вариант
{
public static UserControl LoadAndBuildUpControl(this Page page, string virtualPath)
{
var control = page.LoadControl(virtualPath);
return SapphireControlBuilder.Build<UserControl>(control.GetType());
}
}
Вот мы и справились с поставленной задачей, однако это ещё не всё. А почему теперь не воспользоваться всеми преимуществами Unity, и не внедрить в наш элемент управления AOP времени исполнения с помощью Unity Interception.
К примеру мы можем сделать следующее
{
[HandleException]
public override void DataBind()
{
base.DataBind();
}
}
Это будет означать, что обработка исключений должна добавляться на лету, к тому ж предоставляя нам возможность её изменения во время исполнения, для начала пусть её реализация будет примерно следующая
public class HandleExceptionAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new ExceptionHandler();
}
}
public class ExceptionHandler : ICallHandler
{
/// <exception cref=»SapphireUserFriendlyException»><c>SapphireUserFriendlyException</c>.</exception>
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
var result = getNext()(input, getNext);
if (result.Exception == null)
return result;
throw new SapphireUserFriendlyException();
}
public int Order { get; set; }
}
Ну и конечно же надо сконфигурировать контейнер для создания наших прокси-обработчиков
{
return (T)((Application)HttpContext.Current.ApplicationInstance)
.Container
. AddNewExtension<Interception>()
.Configure<Interception>()
.SetInterceptorFor<T>(new VirtualMethodInterceptor())
.Container
.Resolve<T>();
}
Ресурсы
Sapphire.Application – для чего всё это реализовывалось http://github.com/butaji/Sapphire/tree/master/trunk/Sapphire.Application/
Дэвид предлагает реализации связывания с данными следующего поколения “Databinding 3.0” на основе аналогичного подхода http://weblogs.asp.net/davidfowler/archive/2009/11/13/databinding-3-0.aspx
REPL WebPart для SharePoint
Intro
Сегодня я расскажу о прототипе первого компонента под ярлычком Sapphire. Это REPL WebPart. Эта веб-часть предназначенная для производства оперативных изменений на серверной стороне SharePoint, так же для удаленного исполнения скриптов и тестирования некоторых кусков кода.
PreBody
Производство данной веб-части было инициированно в довольно таки частых потребностях исполнения серверного кода с достаточной оперативностью, в недоступности средств разработки под рукой.
Здесь есть небольшая презетнация, в которой я постарался отобразить принципы работы Repl WebPart:
В добавок к слайдам расскажу о том, что веб-часть представляет собой классический хостинг Dynamic Languages Runtime языков, пока из которых доступен только Python.
Body
Далее хотелось поговорить о том, как же всё это у меня получилось, итак проект включает в себе несколько основных модулей:
- Веб-часть и контролы представления
- Хостинг языков
Далее о них поподробнее
Language Hosting
В связи с тем, что языков, которые я захочу реализовать в данном проекте является неограниченное множество, то конечно же мне необходима абстрактная фабрика для их создания, которая будет скрывать иерархию всех доступных языков и инкапсулировать их под интерфейсом:
{
ILanguage Create(string name);
}
public interface ILanguage
{
string Name { get; }
object Execute(string input);
void SetVar(string name, object value);
object GetVar(string name);
}
Отлично, далее к нашему проекту присоединяются сборки, необходимые для имплементации скриптовых языков:
- IronPython.dll
- Microsoft.Dynamic.dll
- Microsoft.Scripting.Core.dll
- Microsoft.Scripting.ExtensionAttribute.dll
- Microsoft.Scripting.dll
Всё готово для того, чтобы реализовать Python, как среду для исполнения кода, отлично, приступим:
{
private readonly ScriptScope _scope;
public string Name
{
get { return «Python»; }
}
public PythonLanguage()
{
_scope = Python.CreateEngine().CreateScope();
}
public object Execute(string input)
{
ScriptSource source = _scope.Engine.CreateScriptSourceFromString(input);
return source.Execute(_scope);
}
}
Далее немного усложним задачу, в связи с тем, что нам будет необходимо реализовать контекст для работы с SharePoint, выглядеть это должно примерно так:
private void python_should_assume_sharepoint_variables()
{
SPWeb fakeWeb = Isolate.Fake.Instance<SPWeb>();
Isolate.WhenCalled(() => fakeWeb.Title).WillReturn(«I’m fake web»);
var python = _factory.Create(«Python»);
python.SetVar(«__x__», «123″);
python.SetVar(«__web__», fakeWeb);
python.Execute(«__x__ = __web__.Title»);
var x = (string)python.GetVar(«__x__»);
Assert.AreEqual(x, «I’m fake web»);
}
Для работы с объектами SharePoint в из IronPython, нам необходимо добавить несколько библиотек:
_scope.Engine.Runtime.LoadAssembly(typeof(Uri).Assembly);
_scope.Engine.Runtime.LoadAssembly(typeof(SPList).Assembly);
Это будет основой нашей работы с динамическими языками
WebPart + WebControls
Создадим проект с помощью SPVisualDev, добавим в него feature, внутри которой создадим веб-часть Repl WebPart, всё остальные действия стандартные, однако есть небольшой нюанс, нам нужен будет объект, с помощью которого можно будет выводить строковые значения после исполнения. Я решил создать для этих целей объект с классическим названием Console, его реализация предельна проста:
{
private readonly StringBuilder _messageBuilder = new StringBuilder();
public void Write(object message)
{
_messageBuilder.Append(message);
}
public void WriteLine(object message)
{
_messageBuilder.AppendLine(message.ToString());
}
public string Message
{
get
{
return _messageBuilder.ToString();
}
}
}
Мы будем передавать его в среду исполнения языков, а после исполнения кода опрашивать его свойство Message и выводить его содержимое в нашу веб-часть.
EndBody
Все исходники проекта доступны на github: http://github.com/butaji/Sapphire
Так же предварительную версию Sapphire.Environment (решение поставляется в WSP) можно слить на CodePlex: http://sapphire.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=34895
Кратко о Patterns & Practices: SharePoint Guidance
Интро
В данной статье я хочу произвести обзор руководства под названием “SharePoint Guidance” от подразделения Microsoft patterns & practices. Данное руководство предназначено разработчикам/архитекторам SharePoint, в нём описаны основные принципы построения систем на данной платформе. Над руководством трудились выдающиеся представители разработки под SharePoint, а так же он упорно держится в списке самых активных проектов на CodePlex. Далее чуть подробнее.
Описание руководства
Руководство фактически состоит из нескольких аспектов:
- SharePoint Guidance Library – библиотека наиболее используемых и полезных функций, таких как управление конфигурацией, абстрагирование слоя данных, логирование событий и сервисная инфраструктура
- Документация, в которой подробно описаны все принципы построения приведенных примеров, а так же руководства по основным вопросам, возникающим в разработке на SharePoint.
- Contoso Partner Portal Reference Implementation – показательное приложение на MOSS некоей компании Contoso, являющее собой экстранет-портал, в приложении используются практики наиболее приближенные к промышленным решениям.
- Contoso Training Management Reference Implementation – простое приложение HR-отдела, демонстрирующее базовые принципы построения решений на WSS.
- QuickStarts – два небольших примера самых простых приложений на SharePoint, а так же доступа к данным
Помимо исходных кодов и юнит-тестов к ним, примерами так же являются установочные скрипты, билд-скрипты, что фактически является артифактами верно налаженного процесса производства ПО, что очень показательно и полезно с практической точки зрения.
Обзор руководства
1ая версия вышла в прошлом году (Dec2008), в настоящее время доступна 2ая версия данного руководства (Aug2009), о ней и будет идти речь.
- Introduction – небольшое введение, а так же рекомендации к инструментам разработки: VSeWSS, U2U CAML Query Builder, CAML.NET, Typemock Isolator
- Developing SharePoint Applications – основные сценарии разработки под платформу, а так же её ключевые возможности
- Design and Development Guidelines – отображены ключевые моменты архитектуры SharePoint, а так же описаны подходы решения тех или иных задач разработки под платформу; описаны рекомендации к управлению жизненным циклом проектов
- Application and Design Patterns – одна из наиболее интересных глав, в ней описаны как классические паттерны построения корпоративных приложений ложатся на решения под SharePoint
- The SharePoint Guidance Library – описание библиотеки со всеми ключевыми возможностями и компонентами для разработки
- Integrating Line-of-Business Systems – описание интеграции с существующими корпоративными системами
- Considerations for Content-Driven Applications – здесь можно узнать о том, как правильно строить приложения, для хранения контента
- Considerations for Enterprise-Scale Applications – рекомендации по созданию приложений масштаба предприятия
- Considerations for Extranet Development – рекомендации по разработке и планированию экстранет-порталов
- Partner Portal Reference Implementation – описание построения приложения-примера для MOSS
- Training Management Reference Implementation– описание построения приложения-примера для WSS
- QuickStarts – описание приложения для легкого старта работы с SharePoint
Критика
Данное руководство содержит в себе большое количество практичной полезной информации, однако в некоторых технических решениях моя точка зрения расходится с его создателями, в связи с чем мною был начат проект Sapphire: SharePoint Application Framework, который будет так же являться каркасом для построения решений на SharePoint, а так же включать примеры решений и необходимые в повседневной разработке компоненты.
Ресурсы по SharePoint Guidance
Скачать полную версию за август 2009 года можно здесь:
SharePoint Guidance так же представлен на CodePlex: http://www.codeplex.com/spg/ здесь можно задать интересующие вопросы и скачать все последние изменения.
Так же некоторые главы из SharePoint Guidance представлены в видеоряде на channel 9:
- Setting up the Contoso RI – p & p Developing SharePoint Applications guidance
- Walkthrough of the Contoso Reference Implementation- p & p Developing SharePoint Applications guidance
- How to use the configuration component? – p & p Developing SharePoint Applications guidance
- How to use the logging components? – p & p Developing SharePoint Applications guidance
- How to use the SharePoint Service Locator? – p & p Developing SharePoint Applications guidance
Ресурсы по SharePoint
TechNet располагает огромным количеством статей и книг по SharePoint, конечно в них не так много внимания уделено именно разработке, однако для формирования вижна платформы весьма полезно: http://technet.microsoft.com/en-us/library/cc262788.aspx
SharePoint Developer Center здесь можно найти практически всё, что нужно для разработки на SharePoint
Habrahabr, Live Writer и Code Highlighting
Интро
Уже практически полгода я пользуюсь Windows Live Writer в качестве инструмента для написания веб-ориентированных статей. Это отличный инструмент, работающий с большим количеством blogengine’ов, и даже с SharePoint. Но разговор не об этом, в связи с тем, что я разработчик, мне частенько приходится вставлять листинг кода в написанные статьи, на это я и хочу обратить ваше внимание.
Пигменты
Многие представители Python-сообщества наверняка знакомы с дивным проектом под названием Pygments:
Ну так вот, товарищ Harry Pierson (@DevHawk) скомпилировал этот движок под IronPython и написал обертку-плагин для Windows Live Writer (качать здесь не торопиться). Отлично, однако то, что получается в результате его обработки не выделяется цветом на habrahabr, это связанно с некоторыми особенностями ресурса, однако поправимо.
Данными работами я и предлагаю заняться, для этого нам понадобятся:
- Исходники проекта (на GitHub’е)
- 5 мин времени
После того, как мы скачали исходники, стоит разыскать скрипт под именем pygments_package\devhawk_formatter.py и немного его подкоректировать:
# colors are readily specified in hex: ’RRGGBB’
if style['color']:
start += ‘<span style=»color:#%s«>’ % style['color']
end = ‘</span>’ + end
заменить на
# colors are readily specified in hex: ’RRGGBB’
if style['color']:
start += ‘<font color=»#%s«>’ % style['color']
end = ‘</font>’ + end
Далее пересобрать с помощью скрипта build.bat проект и запустить инсталлер – теперь то что делает данный проект в цвете отображается на хабре.
Вы подготовились к приходу AutoMapper?
Введение
Данная статья предназначена к прочтению разработчикам и архитекторам распределенных систем на платформе .NET. В ней будет рассмотрен гибкий каркас для объектно-объектного преобразования (далее маппинга). Так же будут рассмотрены некоторые аспекты Domain-Driven Design’а.
Зачем мне нужен объектно-объектный маппинг?
Следуя основным принципам DDD, мы реализуем так называемую Rich Domain Model (эти объекты также должны соответствовать принципу POxO). Объекты реального мира, нашедшие отражение в нашем приложении частенько так же передают достаточную сложность, следовательно, достаточно корректно построенная модель крайне тяжело поддаётся перемещению между слоями приложения (не путать с легкостью вносимых изменений).
Тем не менее достаточно часто появляется необходимость “распределения” (я имею ввиду создание промежуточных сущностей, а не растекание модели по слоям) модели между слоями, для отображения, к примеру, атрибутов её сущностей пользователям (в шаблонах представления MVx), а так же передаче по сервисам (Data Transfer Object). Порой бывает даже, что модель “распределяется” для тестирования некоторых аспектов.
Предположим, мы в Африке, у нас банановая плантация, всё классно, выращиваем, продаём, выращиваем, продаём, но тут внезапно внутренний рынок переполняется и нам надо расширятся (к примеру вести бананы в Россию), мы пишем WCF сервис, который будет слать наши бананы. Так как бананы в Африке имеют несколько иное значение, чем в России, то, соответственно нам понадобятся лишь некоторые атрибуты (остальные фактически не имеют значения), которые мы забубеним в наш DTO
Правильнее было бы дать классу BananaWrapper название BananaDTO, для того, чтобы точно отображать его функциональное назначение, но я оставлю название таким для большего уровня абстракции, к примеру, если нам понадобится сделать автомат по продажи бананов и поместить этот объект в Presenter Model
Хочу заметить, что порой задача преобразования объектов становится довольно-таки нетривиальной и в лучшем случаем выглядит примерно подобным образом (это решение в лоб, есть ещё более изощренные методы
):
- public class Banana
- {
- public string Country { get; set; }
- public double Price { get; set; }
- public int Age { get; set; }
- }
- public class BananaWrapper
- {
- public string Country { get; set; }
- public double Price { get; set; }
- public int Age { get; set; }
- }
- public class BananaMapper
- {
- public BananaWrapper GetWrapper(Banana banana)
- {
- return new BananaWrapper
- {
- Country = banana.Country,
- Price = banana.Price,
- Age = banana.Age
- };
- }
- }
* This source code was highlighted with Source Code Highlighter.
Думаю, что такой код писать, а тем более сопровождать, мало кому будет в радость, в последнее время я как раз частенько встречался с такого рода задачами, и находился в поиске решения проблемы.
AutoMapper
И тут на сцену выходит наш персонаж – AutoMapper, и сразу же говорит мне: – послушай, ты что такое пишешь? тебе не лень? ты не боишься допустить ошибок? хочешь я тебе помогу?!. Я конечно же соглашаюсь, и получаю в ответ следующее решение моей проблемы:
- public class BananaMapper
- {
- public BananaMapper()
- {
- Mapper.CreateMap<Banana, BananaWrapper>();
- }
- public BananaWrapper GetWrapper(Banana banana)
- {
- return Mapper.Map<Banana, BananaWrapper>(banana); ;
- }
- }
* This source code was highlighted with Source Code Highlighter.
Класс, и это действительно всё, что мне понадобится. Сложность, вышележащего примера снизилась в моих глазах до нуля.
Итак, что же за механизмы лежат внутри AutoMapper?
AutoMapper проверяет есть соответствующие поля в указанных типах, соответствие проводится как по имени свойства, так и по его типу. Даже такие нюансы, как Product.Name и ProductName будут учтены и обработаны автоматически (wow!). Плюс ко всему методы GetXXX() будут ложится на свойства XXX (да, ну и естественно для особо раздражительных все эти прелести можно отключить и переопределить всё в своих собственных таблицах соответствия (далее мапках)).
Кастомная конфигурация выглядит примерно следующим образом:
- Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
- .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
- .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour))
- .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute));
* This source code was highlighted with Source Code Highlighter.
Кстати, все ваши кастомные конфигурации легко поддаются проверке с помощью следующего метода:
- Mapper.AssertConfigurationIsValid();
* This source code was highlighted with Source Code Highlighter.
Так же не плохо работает с:
- коллекциями
- вложенными мапами
- конверторами типов (к примеру string в int)
- нестандартной логикой инициализации
- форматированием
История
Проект появился в конце’08-начале’09, около полугода находился в версии 0.31, сейчас же добрался до RC 1.0, думаю, что релиз уже совсем скоро.
Overhead?
Дебаты по поводу того, насколько быстрее будет работать AutoMapper и присвоение свойств в ручную (и прочие мульки) я игнорирую, т.к. готов пойти на любые жертвы производительности, если получу ясный, читабельный код. Ах, да, автор AutoMapper позаботился об этих вопросах и написал бенчмарки, смотреть здесь: http://code.google.com/p/automapperhome/source/browse/#svn/trunk/src/Benchmark
Ресурсы
Скачать проект, а так же ознакомиться с исходными кодами можно здесь: http://code.google.com/p/automapperhome/
Обсуждения каркаса здесь: http://groups.google.com/group/automapper-users
Так же примеры использования есть здесь: http://automapper.codeplex.com/
Кстати проект разрабатывает Jimmy Bogard, который так же пишет BDD фреймворк для .NET под названием NBehave.
DynamicObject, JSON и ближайшее будущее
Введение
В данной статье хочу ознакомить вас с небольшим приложением для работы с JSON данными, демонстрирующим возможности, доступные нам в .NET 4.0. Поверхностно будут рассмотрены вопросы JSON-формата, а так же работы с динамическими типами данных.
JSON
Есть такая классная шутка, известная широким массам:
JSON (JavaScript Object Notation) – простой формат обмена данными, удобный для чтения и написания как человеком, так и компьютером. Он основан на подмножестве языка программирования JavaScript, определенного в стандарте ECMA-262 3rd Edition – December 1999. JSON – текстовый формат, полностью независимый от языка реализации, но он использует соглашения, знакомые программистам C-подобных языков, таких как C, C++, C#, Java, JavaScript, Perl, Python и многих других. Эти свойства делают JSON идеальным языком обмена данными. далее…
JSON и ваше приложение
Ну так вот, обычно, получая объекты по JSON в наших приложениях мы должны подготовить инфраструктуру для их поддержки (к примеру с помощью DataContractJsonSerializer и решения на типа этого). Однако это занимает значительное время у разработчика. В связи с этим у меня появилось страстное желание поставить JSON механизмы на рельсы динамических возможностей .NET 4.0 и получать от работы с ним одно удовольствие
DynamicObject
DynamicObject – Предоставляет нам простой класс, наследуясь от которого мы можем получить динамическое поведение объекта на этапе исполнения. Наследуясь от этого класса и переопределяя некоторые его методы реализуется вся основная логика необходимая нам для этого.
Если хостинг DLR и прочие прелести вас заинтересовали и вы хотите ознакомиться немного поглубже с вопросом, то можно посмотреть наше выступление здесь, а так же слайды (здесь и здесь).
К разработке
Для того, чтобы не испытывать неудобств при работе с JSON, я предлагаю воспользоваться (слить и зареференсить) решением от James Newton под названием JSON.NET, данный проект свободен и удовлетворяет всем основным требованиям работы с JSON в рамках .NET-стека (в том числе и LINQ).
Да, к тому же нам понадобится IDE, умеющая работать с .NET 4.0b1, к примеру Visual Studio 2010 Beta 1 (кстати #develop не отстает).
Создаем наше приложение, которое будет выглядеть примерно следующим образом:
""500 gigabyte hard drive"" ]}»;
dynamic computer = new DynamicJSON(input);
И пробуем посмотреть, какие же свойства обнаружатся у нашего компьютера:
«Intel»
>> computer.Drives
[
"DVD read/writer",
"500 gigabyte hard drive"
]
>> computer.Drives[0]
«DVD read/writer»
Пока ничего необычного, учитывая, что мы не знаем, что же из себя представляет DynamicJSON, к реализации которого мы и обратимся за впечатлениями:
public class DynamicJSON : DynamicObject
{
private JObject _data;
public DynamicJSON(string data)
{
_data = JObject.Parse(data);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _data[binder.Name];
return true;
}
}
И это всё, что потребуется от нас для работы данного примера, меня подобные вещи радуют чрезмерно, в связи с чем рекомендую и вам использовать подобную практику в своих решениях.
Языки предметной области Domain-Specific Languages (DSL)
Что это?
Это некоторая форма компьютерных языков, разрабатываемых для специфичной предметной области. Это то, что позволяет вам (разработчикам ПО) лучше взаимодействовать с носителями “доменных знаний”. А так же позволяет более лаконично оформлять бизнес-логику. Это то, что представляет собой, к примеру, SQL, Linq, многое из синтаксиса Ruby On Rails.
Зачем мне это?
Если вы согласны с утверждением: “Языки общего назначения порой слишком красноречивы”, вы разрабатываете на .NET, либо сильно интересуетесь программированием, то наш доклад будет вам интересен.
Что я узнаю?
Ответы на следующие вопросы:
- Что такое DSL?
- Откуда это понятие пришло к нам?
- Какие бывают DSL?
- Какие “языки общего употребления (GPL)” предоставляют возможности построения DSL? Какие из них есть на .NET?
- Почему я должен использовать DSL? Какие плюсы от этого?
- Какие шаблоны используются при построении DSL?
- А можно увидеть примеры?
Материалы нашего выступления
Слайды презентации
Building DSLs on CLR and DLR (.NET)
Видео:
http://video.yandex.ru/users/thecoffee/collection/1/
Видео в более пригодном к рассматриванию надписей на доске качестве можно слить по ссылкам ниже:
http://narod.ru/disk/9278634000/01.wmv.html
http://narod.ru/disk/9279885000/02.wmv.html
Все рассмотренные примеры доступны здесь:
Учимся проектировать на основе предметной области (DDD: Domain Driven Design)
1. Введение
В данной статье я хотел бы рассказать об этих трёх буквах, постоянно находящихся на слуху, но для многих являющихся тайной за семью печатями, а так же привести ряд ресурсов, с которыми неплохо было бы познакомиться при желании продолжить развитие в проектировании на основе предметной области (DDD: Domain Driven Design).
2. Так почему же DDD?
Есть несколько шаблонов реализации предметной области (Domain Logic) или бизнес-логики (Business Logic):
1) Table Module – представляет собой объект, в единственном экземпляре, обрабатывающий бизнес логику для всех записей в таблице базы данных, либо представления.
2) Transaction Script – организует взаимодействие с бизнес-логикой посредствам процедур, принимающих запросы с уровня представления.
3) Domain Model – непосредственно, объектная модель предметной области, включающая в себя как поведение, так и данные.
Эти шаблоны описаны более подробно Мартином Фаулером, в его книге “Архитектура корпоративных программных приложений. Шаблоны корпоративных приложений” (Patterns of Enterprise Application Architecture (P of EAA)). В данной книге он показывает, что первые два шаблона более привлекательны в начале работы с предметной областью, однако так же обращает внимание, что при наращивании сложности логики предметной области стоит больше внимания уделять сопровождению инфраструктуры, используя первые два подхода, это время можно уменьшить, если обратиться в своём решении к третьему из вышеперечисленных шаблонов, так называемой “Модели предметной области”.
На основе этого сделаем небольшой вывод о том, что данный шаблон (“Модель предметной области”) лучше всего подойдёт, к примеру, для такой непростой области, как финансовый рынок. Большинство, создаваемого в наши дни программного обеспечения предназначено для различных нужд бизнеса, следовательно какие-то абстрактные, обобщенные решения находят своё место на рынке (с довольно таки высокой конкуренцией) всё реже и реже. К чему я пишу про всё это? Потому что DDD – это не только качественное проектирование, но так же и показательный пример того, как следует выделить предметную область в программном обеспечении, для того, чтобы проще преодолевать сложности, частые изменения, проблемы коммуникации и прочие недуги предметной области, вместо того чтобы разрабатывать уродливую, сложную для понимания систему, в которой любое изменение или исправление способно обрушить на вас лавину всё новых и новых дефектов.
DDD ни в коем случае не отрицает наследия практик разработки, таких как:
-
Шаблоны проектирования (Design Patterns) (в том числе всем известные GoF)
-
Так называемый ряд принципов проектирования S.O.L.I.D, собранных Робертом “Uncle Bob” Мартином.
-
Разработка по средствам тестирования (TDD: Test-Driven Development)
DDD лишь дополняет их. Поиск подходящей модели и абстракций в сложных сценариях требует значительных знаний в сфере объектно-ориентированного подхода, и достаточного опыта применения различных принципов, шаблонов и практик, а не просто DDD, как может показаться.
3. С чего можно начать?
Если мой “нудный PR” проектирования на основе предметной области (DDD) вас до сих пор не утомил, то думаю нам стоит продолжить, если же иначе, то посмотрите хотя бы ссылки на материалы.
Первой книгой пролившей свет на DDD для широкой публики была так называемая “Большая синяя книга” (мем. BBB: Big Blue Book): Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans (на русский язык пока не переведена).
Книга довольна подробно рассказывает о том, что из себя представляет DDD, и все связанные аспекты, такие как: язык предметной области, шаблоны, практики проектирования, рефакторинг, моделирование, как сделать разработку гибкой и многое другое. Но даже если вы ознакомитесь со всеми вопросами, поднятыми в книге (что является не совсем простым занятием), вы обратите внимание, что вопросы рассматриваются только с теоретической точки зрения, оставляя весь простор для практики (книга не привязана к конкретной платформе разработки).
Для большинства из нас чтение чистой теории, без подкрепления практическими примерами не нравится, в связи с этим можно обратить своё внимание на сокращенную (и свободную для доступа) версию этой книги, подготовленную порталом InfoQ: Domain Driven Design Quickly.
Есть так же несколько хороших презентаций Эрика Ивенса (Eric Evans), с которых можно начать:
1) DDD: putting the model to work
2) Eric Evans on DDD: Strategic Design
На портале InfoQ можно найти множество других презентаций, статей и интервью, посвященных DDD.
Итак, с теоретической частью мы разобрались, где же можно найти примеры практического применения DDD? Отличной книгой для этого является .NET Domain-Driven Design with C#, Problem – Design – Solution написанная Tim McCarthy.
В этой книге вы наёдете практические примеры:
1) Как проходит процесс проектирования и разработки, от определения требований, до написания кода
2) Как организовывать архитектурные слои в своих решениях
3) Как применять шаблоны и практики DDD
4) Как построить небольшой каркас для DDD
5) Как изолировать домен предметной области от модели
6) Современные паттерны представления данных и взаимодействия с ними (Model-View-ViewModel) в такой среде как WPF (так же применимы к Silverlight) в практики.
Эта книга – отличный практикум по DDD, содержащий очень широкий пласт идей. Начинается книга с разработки требований, а заканчивается реализацией промышленного приложения, исходные коды которого доступны на Codeplex.
Вся концепция книги построена на 3 книгах-столпах DDD:
- PoEAA Мартина Фаулера
- DDD Эрика Ивенса
- Applying Domain – Driven Design and Patterns by Jimmy Nilsson’s (“Применение шаблонов проектирования: проблемно-ориентированное проектирование приложений с примерами на C# и .NET” Джимми Нильссона )
В этой книге поверхностно рассмотрены все вопросы, техники и паттерны, применяемые в DDD, все примеры сопровождаются кодом, что упрощает понимание. Книга превосходная, однако русский перевод подкачал, поэтому, рекомендую прочитать оригинал.
Однако DDD – это не просто практические решения или шаблоны, это мышление и подход, и есть великое множество нюансов, которые необходимо учитывать, если вы решили следовать DDD, таких как: фокусирование на высокий приоритет отдается модели, выработка языка предметной области, контекст модели, процесс моделирования, разделение знаний, рефакторинг, стратегический дизайн и т.д…это является основной причиной ознакомиться с книгой Эрика Ивенса, так как она даст вам более объемное и глубокое понимание философии DDD.
DDD не привязанны к конкретной технологии, однако соблюдать DDD будет не так просто, без наличия хороших средств и практик в вашем арсенале, таких как: TDD-фреймворк, ORM, возможность реализации независимости сохраняемости (Persistence Ignorance), IoC-контейнер (Inversion of Control), и возможностей AOP (Аспектно-Ориентированного Программирования), конечно не значит, что все эти инструменты нам понадобятся, однако они приблизят нас к реализации DDD на практике. Практичная ценность этих средств в том, что они позволять изолировать модель предметной области, что является ключевой целью DDD. Книга Джимми Нильссона может познакомить вас с возможностями и видами данных инструментов. Джимми так же показывает как использовать шаблоны реализации корпоративных приложений, и строить, благодаря им, цельное решение, основанное на современных инструментах и практиках.
Некоторые реализации шаблонов DDD на Ruby On Rails:
Some DDD (Domain Driven Design) Concepts implemented in Rails
4. Актуальные вопросы DDD
C DDD так же тесно связана такая тема, как DDDD: Distributed Domain Driven Design (Распределенный DDD). DDDD – это DDD в распределенных сценариях. В настоящее время существует не так много ресурсов, посвященных DDDD, в нескольких словах о DDDD: покрывает проблему реализации сообщений и DDD, разделение команд и запросов (Command Query Separation (CQS)) помогает реализовать данный подход. Грег Янг (Greg Young) сообщил, что готовит книгу, посвященную DDDD.
SOA и DDD – это ещё одна объемная тема, часто обсуждаемая Udi Dahan
5. DDD шаблоны, концепции и понятия
В промышленных приложениях DDD использует ряд шаблонов, часть которых описана в книге Эрика Ивенса, но, это не отменяет применение объектно-ориентированного подхода, включающего GoF-шаблоны, шаблоны Мартина Фаулера, описанные в его PoEAA, Шаблоны интеграции корпоративных приложений и т.д.…
Вот некоторые из них:
- Value Object
- Entity
- Service
- Module
- Aggregate
- Repository
- Specification
- Layer Supertype
- Identity Map
- Factories
- Unit of Work(UoW)
- Inversion of Control(IoC)
- Aspect Oriented Programming(AOP)
- Bounded Context
- Ubiquities language
- Object-relational mapping(ORM)
- Anemic Domain Model anti pattern
6. Примеры приложений
Найти хорошие примеры реализации DDD очень сложно, не потому что их не существует, а потому что реальная сила DDD реализуется в приложениях, используемых в довольно сложных областях, и, как правило, являющихся коммерческими проектами. Однако можно найти несколько неплохих проектов, в которых можно проследить некоторые идеи реализации шаблонов DDD.
Вот они:
1) Приложение Тима Маккарти его проект, описанный в деталях в его книге. Он описывает не только применение шаблонов, но так же акцентирует внимание в разработке модели предметной области с точки зрения DDD.
Проект так же интересен тем, что построен на .NET 3.5 и демонстрирует всю силу современного подхода связывания данных с моделью предметной области (data binding, реализация шаблона MVVM). Так же его стиль примечателен умением выделять абстракции и повторно используемый код.
2) Следующий проект, на который следует обратить внимание – это приложение разработанное Yves Goeleven, создание данного приложения описано в его блоге (так же посвященному основным концептам DDD). Другим его приложением является DDD-каркас. Следует обратить внимание на его реализацию взаимодействия шаблонов Repository и Specification.
3) Billy McCafferty разрабатывает потрясающий open source фреймворк, сфокусированный на DDD, под названием S#arp Architecture. У него есть очень хорошее описание, включающее в себя описание шаблонов и подходов, заключенных в фреймворке. Фреймворк нацелен на разработку ASP.NET MVC приложений с применением NHibernate.
4) C# Domain-Driven Design sample application ( ndddsample ), это приложение, разрабатываемое Джимми Нильссоном, демонстрирует разбиение приложения на ключевые слои с точки зрения DDD. Так же демонстрируется практическое применение шаблонов building block в предметной области перевозки грузов, описанной в его книге.
Этот проект основан на совместной работе компании Эрика Ивенса “Domain Language” и шведской консалтинговой компании “Citerus”.
Цель этого проекта:
- Показать практическое применение использования DDD с применением .NET.
- Использование актуальных утилит, технологий и методологий разработки в области .NET, обсуждаемых ALT.NET-коммунити.
- Привести практические примеры реализации типовых DDD приложений.
- Показать способ реализации DDD на конкретной платформе, что позволит без труда осуществить тоже самое на любой другой платформе.
- Помочь в выборе реализуемых практик. Различные подходы позволят сообществу обсудить их и выбрать соответствующий для конкретной реализации.
здесь более подробная информация.
7. Ресурсы по Domain Driven Design
Официальный сайт – http://domaindrivendesign.org/
Группа обсуждений – http://tech.groups.yahoo.com/group/domaindrivendesign/ это взрослая группа, очень хороший источник идей, место для обсуждений всех видов проблем в области DDD. В ней на ваши вопросы могут ответить опытные в DDD люди, даже Эрик Ивенс
.
Блог Jimmy Bogard’а – http://www.lostechies.com/blogs/jimmy_bogard/default.aspx
Блог Colin Jack’а – http://colinjack.blogspot.com/
Блог Greg Young’а – http://codebetter.com/blogs/gregyoung/default.aspx
Блог Casey Charlton’а – http://devlicio.us/blogs/casey/
Блог Udi Dahan’а – http://www.udidahan.com/
Введение в Domain-Driven Design – http://msdn.microsoft.com/ru-ru/magazine/dd419654.aspx
8. Заключение
Если вы заинтересованы в расширении ваших “объектно-ориентированных горизонтов” в сложных корпоративных системах и изучении новых способов разработки и проектирования, то DDD – именно то что нужно.
http://weblogs.asp.net/arturtrosin/archive/2009/02/09/domain-driven-design-learning.aspx
Делаем code-background-color persistance в Visual Studio 2010
Сегодня утром пришло письмо с connect.microsoft.com, куда я запостил баг о том, что VS2010 не умеет сохранять настройку цвета фона. И вот что, данный баг пофиксен:
Цитирую:
“For now, there is an extension written by a member of the VS Editor team as a patch that will allow custom background colors to persist. You can find it in VS by going to Tools->Extension Manager and searching for the "BackgroundPatchExtension," or download it from a web browser at this link: http://visualstudiogallery.msdn.microsoft.com/en-us/d53e0db3-6c41-4cf5-8ca1-339aeef1ac30. Once you install it, just make sure it is enabled in the Extension Manager, and your background color should be applied correctly.”
Надеюсь для кого-нибудь это будет полезным.
Пример практики BDD при работе со Specter Framework
Specter – инфраструктура для составления объектно-поведенческих спецификаций для .NET. Он предоставляет возможности для обеспечения разработки, руководствуясь поведением системы (BDD), требуя от разработчиков написания исполняемой спецификации для объектов перед написанием самих объектов. Технически это ни чем не отличается от разработки по средствам тестирования (TDD), хотя различия в форме написания снимают психологический барьер для написания “тестов” для кода, которого ещё не существует. Есть множество проектов для различных платформ, реализующих данную идею (К примеру RSpec для Ruby, NSpec для .NET. Подробнее о средах здесь).
Specter использует возможности мета-программирования языка Boo (CLR .NET) для написания неплохо читаемых спецификаций.
Пример практики BDD при работе со Specter
Для нашего примера рассмотрим спецификацию мини-бара Бендера, она будет выглядеть следующим образом:
import Specter.Framework
import Bender
context "At Bender's bar":
_bar as duck #our subject is defined in the setup block below
setup:
subject _bar = Bender.MiniBar()
#one-liner shorthand
specify { _bar.DrinkOneBeer() }.Must.Not.Throw()
specify "If I drink 5 beers then I owe 5 bucks":
for i in range(5):
_bar.DrinkOneBeer()
_bar.Balance.Must.Equal(-5)
specify "If I drink more than ten beers then I get drunk":
for i in range(10):
_bar.DrinkOneBeer()
{ _bar.DrinkOneBeer() }.Must.Throw()
* This source code was highlighted with Source Code Highlighter.
Хотелось бы отдельно отметить возможность читаемости данного кода сторонними от программирования людьми.
Что же мы такое написали?
Всё очень просто, мы создали привычный нам по NUnit TextFixture Class и описали Test методы. Сейчас расскажу, как это получилось. Обратим внимание на следующую строчку:
context "At Bender's bar":
* This source code was highlighted with Source Code Highlighter.
Мы определяем некий context. Что же он из себя представляет? На самом деле, если полезть рефлектором в сборку, которую мы полчим, при компиляции спецификации, мы обнаружим, что данный контекст являет собой сам TextFixture Class:
[NUnit.Framework.TestFixture]
class EmptyStack:
* This source code was highlighted with Source Code Highlighter.
Далее посмотрим на:
setup:
subject _bar = Bender.MiniBar()
* This source code was highlighted with Source Code Highlighter.
Это являет собой привычный нам SetUp:
[NUnit.Framework.SetUp]
public void SetUp()
{
subject _bar = Bender.MiniBar();
}
* This source code was highlighted with Source Code Highlighter.
Следующая интересная нам строчка:
specify { _bar.DrinkOneBeer() }.Must.Not.Throw()
* This source code was highlighted with Source Code Highlighter.
Является уже тестом:
[NUnit.Framework.Test]
public void BarDrinkOneBeerMustNotThrow()
{
Assert.DoesNotThrow(_bar.DrinkOneBeer());
}
* This source code was highlighted with Source Code Highlighter.
Аналогично следующие строки соответствуют:
specify "If I drink 5 beers then I owe 5 bucks":
for i in range(5):
_bar.DrinkOneBeer()
_bar.Balance.Must.Equal(-5)
* This source code was highlighted with Source Code Highlighter.
Так же тестам:
[NUnit.Framework.Test]
public void IfIDrink5BeersThenIOwe5Bucks()
{
for (int i = 0; i == 5; i++)
_bar.DrinkOneBeer();
Int32MustModule.Must(_bar.Balance, “Bar balance must equal -5").Equal(-5);
}
* This source code was highlighted with Source Code Highlighter.
И ещё одна строчка:
specify "If I drink more than ten beers then I get drunk":
for i in range(10):
_bar.DrinkOneBeer()
{ _bar.DrinkOneBeer() }.Must.Throw()
* This source code was highlighted with Source Code Highlighter.
Соответствует:
[NUnit.Framework.Test]
public void IfiDrinkMoreThanTenBeersThenIGetDrunk()
{
for (int i = 0; i == 10; i++)
{
_bar.DrinkOneBeer();
}
Assert.Throws((typeof(InvalidOperationException), _bar.DrinkOneBeer()); }
* This source code was highlighted with Source Code Highlighter.
Со спецификацией покончено, далее нам необходимо написать КОД нашего приложения, т.к. specter выругался о том, что спецификация не реализована:
Что же, реализуем наш мини-бар:
namespace Bender
class MiniBar:
pass
* This source code was highlighted with Source Code Highlighter.
И добавляем необходимый метод:
namespace Bender
class MiniBar:
def DrinkOneBeer():
pass
[getter(Balance)]
_balance = 0
* This source code was highlighted with Source Code Highlighter.
Но тем не менее specter не доволен, так как наш метод не реализует работу с балансом, описанную в спецификации:
Придётся добавить реализацию:
namespace Bender
class MiniBar:
def DrinkOneBeer():_balance-- if _balance < -10: raise System.Exception("i'm drunk")
[getter(Balance)]
_balance = 0
* This source code was highlighted with Source Code Highlighter.
По-моему specter’у всё понравилось:
Отлично, вот мы и попрактиковали BDD.
Итак, мы получаем привычные нам unit-тесты, однако мотивация написания немного измениться, а так же нами будет приобретена такая возможность, как предоставление specter-спецификации тестов в качестве документации. Мне кажется это отличная идея!
Ресурсы
О BDD почитать можно здесь:
http://habrahabr.ru/blogs/testing/52929/ (про BDD на русском на примере RSpec, в конце статьи есть ссылки)
Знакомство с Behavior Driven Development (BDD) (рус.)
Скачать Specter можно здесь:


