Пишем клиента для OData на Python

Open Data Protocol

Что это такое и для чего создан очень хорошо рассказано в этом видео: http://www.msteched.com/2010/NorthAmerica/DEV208

Python

Одним из аргументов в пользу OData звучит то, что данный протокол не зависит от платформы, и это очевидно (AtomPub), а так же не привязан к конкретному вендору. Но всё же я решил набросать небольшой пример, в котором эмулировать привычный .NET API для работы с OData

Lets code it

import odata

url = "http://odata.netflix.com/Catalog/"

service = odata.service(url)

service.Languages

languages = service.execute()

print languages

Давайте разбираться, что же написано выше.

http://odata.netflix.com/Catalog/ - netflix является одним из официальных поставщиков OData

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

odata.py

import urllib2
import feedparser

version = '0.1'

feedparser — не входит в набор стандартных библиотек python, поэтому её надо будет скачать и установить, как видно из нахвания библиотека предоставляет гибкий API для доступа к различным лентам данных, а следовательно отлично подходит для нашей будущей реализации

class service(interceptor):
    def __init__(self, url):
        self.url = url

Реализация класса service (фактически data-proxy), который будет основной точкой доступа для работы с данными.

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

Внимательный читатель мог заметить, что service наследует от некоторого класса interceptor, вот он:

class interceptor(object):
    def __getattribute__(self, name):
        try:
            return object.__getattribute__(self, name)
        except:
            self.property = name

Из реализации видно, что наш класс занимается тем, что отлавливает вызов несуществующих свойств, к примеру наш Languages и записывает последний в поле property.

Следующей итерацией необходимо имплементировать вызов метода получения данных execute()

class service(interceptor):
    def __init__(self, url):
        self.url = url

    def execute(self):
        proc_url = self.url + "/" + self.property
        result = feedparser.parse(proc_url)
        for entry in result.entries: print entry.title

В данном методе мы формируем url-строку для запроса ленты данных, в последствии пробегаемся по результату и выводим значения клиенту.

Это лишь начальная стадия реализации, многие вопросы не учтены (фильтрация данных, построение запросов и прочее), однако данный пример наглядно указывает на общеупотребимость Open Data Protocol в повседневных нуждах разработки.

Что почитать

Adventures in Meta programming in Python: im_class, __import__, __getattribute__ http://www.lostechies.com/blogs/rssvihla/archive/2010/01/08/adventures-in-meta-programming-in-python-im-class-import-getattribute.aspx

Introducing a Ruby OData Client Library http://blogs.visoftinc.com/archive/2010/06/12/Introducing-a-Ruby-OData-Client-Library.aspx

LINQPad теперь тоже понимает OData http://www.linqpad.net/Beta.aspx

Создание OData API для StackOverflow включая XML и JSON за 30 минут http://blog.zzlab.ru/perevod/sozdanie-odata-api-dlya-stackoverflow-vklyuchaya-xml-i-json-za-30-minut.html

Что такое API данных Google? http://code.google.com/intl/ru-RU/apis/gdata/


6й подкаст Петербургской группы ALT.NET

Сообщества Разработчиков

Ведущие: Дмитрий Нестерук twitter и Виталий Баум twitter

Наши гости: Владимир Юнев и Евгений Жарков

Сообщества в рунете

За пределами рунета

Подкасты Петерубргской Группы Alt.Net

Подкасты Петерубргской Группы Alt.Net

Подкасты о разработке в среде .Net. Ключевые слова: C#, F#, Boo, Visual Studio, .Net, PostSharp, Asp.Net личная подкаст-лента Петербургская Группа Alt.NetПетербургская Группа Alt.Net (подробнее, RSS-поток)


Полноценный Behavior Driven Development на .NET

Введение

Меня переполняют эмоции. Это связанно с тем, что сегодня мой товарищ сообщил о следующем: есть полноценная реализация BDD движка для .NET.

Называется проект SpecFlow

Исходный код ныне базируется на GitHub http://github.com/techtalk/SpecFlow

Немного теории

Вопрос о том, что же такое Behavior Driven Development неоднозначен. Особенно противоречивы его отношения с Test Driven Development. Однако предлагаю абстрагироваться от сравнения двух подходов и договорится, что суть этого есть одно и тоже, т.к. особенных различий нет. Основной чертой выделяющей BDD от TDD по-моему усмотрению является ориентация на различных участников процесса производства программного обеспечения, будь то менеджер, разработчик, тестер, заказчик. Вы вместе пишете тесты, и вместе можете развивать проект в нужном направлении и вполне понятных терминах. Так же на ум в данном случае приходит такой термин, как Ubiquitous Language из Domain Driven Design. А ведь и в самом деле, обобщение терминологии описания вашей предметной области вполне может ложится на практику BDD.

Меня в BDD больше всего прельщает возможность описания функциональности на разговорном языке, что гарантирует актуальность документации (т.к. ваши behavior тесты и будут фактически вашей документацией). Так же при написании тестов на подобном уровне вы четко сконцентрированы на поставленной задаче, а не способах её разрешения и применяемых инструментах, что так же весьма полезно для качества как вашего дизайна, так и соответствии программного продукта назначению.

Все известные инструменты (к примеру mock-объекты) и практики (такие как шаблоны тестирования), используемые вами при разработке в стиле TDD, изменятся незначительно при переходе на BDD стиль, в связи с чем можно начать применять BDD уже сегодня. Так же вполне возможно, что вам необходимо будет произвести более низкоуровневое тестирование некоторых компонентов, что несомненно приведёт к смешиванию двух практик, что несомненно должно происходить осознано и с должен вниманием.

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

Инструменты

cuke_logo[1] На данный момент существует множество BDD framework’ов, однако “наиболее верным” с точки зрения родоначальников данного термина (Dan North, David Chelimsky, Aslak Hellesøy) является такой проект как: Cucumber (ранее известный как RSpec). Этот проект написан для Ruby. Большинство RubyOnRails разработчиков (как основные представители ruby сообщества) пользуются именно этим фреймворком для тестирования своих продуктов.

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

Инструменты для .NET в основном копируют формат и принципы, заложенные в Cucumber, их существует несколько:

  • SpecFlow – предмет сегодняшнего разговора (из плюсов – аналогия с Cucumber и интеграция в Visual Studio 2008 и 2010)
  • NBehave – работа с данным фреймворком близка к возможностям Cucumber в настоящее время (до этого он так же был в разряде “многословных” и  это, наверное, наиболее распространенный BDD фреймворк среди .NET разработчиков)
  • NSpecify, NSpec – довольно-таки “многословные” фреймворки для BDD
  • Specter – аналогично, примечателен тем, что написан на Boo

Теперь практика

Для того, чтобы проиграться с этим замечательным инструментом нам понадобятся:

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

Создавать мы с вами будет стандартный пример для подобного рода фреймвороков, а именно Калькулятор.image

Создадим новый solution в студии, в котором будет два проекта.

  1. Calculator
  2. Calculator.Tests

Для того, чтобы его описать в решении + подготовить реализацию нам понадобится немного инфраструктурных заморочек в проекте Calculator.Tests (ниже работа пока будет только с ним):

  • добавить reference на TechTalk.SpecFlow.dll (которая скорее всего окажется в папке с установленным SpecFlow)
  • добавить reference на nunit.framework.dll (его следует искать в GAC’е)

Теперь всё готово для того, чтобы погрузиться в мир BDD. Откиньтесь на спинки кресел и расстегните ремни.

В пункте Add New Item у вас наверняка появились новые пункты, начинающиеся на SpecFlowimage

Далее задайте имя нашей фиче addition.feature и щелкните Add.

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

http://github.com/aslakhellesoy/cucumber/blob/master/examples/i18n/en/features/addition.feature# language: en

Feature: Addition

  In order to avoid silly mistakes

  As a math idiot 

  I want to be told the sum of two numbers

  Scenario Outline: Add two numbers

    Given I have entered <input_1> into the calculator

    And I have entered <input_2> into the calculator

    When I press <button>

    Then the result should be <output> on the screen

  Examples:

    | input_1 | input_2 | button | output |

    | 20      | 30      | add    | 50     |

    | 2       | 5       | add    | 7      |

    | 0       | 40      | add    | 40     |

Жмём ctrl+s и большая часть работы по описанию выполнена.

Теперь нам надо сделать описание steps, для того, чтобы связать сценарий с тестируемым модулем. Для этого добавьте новый элемент SpecFlow Step Definition, назовём мы его calculator_steps.cs.

Данный файл уже содержит в себе некоторые записи по-умолчанию, в принципе они вполне подходят для нашего примера. (Кстати, обратите внимание как строго упорядочены все методы по шаблону AAA: Arrange, Act, Assert, в данной интерпретации Given, When, Then)

Однако немного их всё же придется изменить.

Внутри данного метода:

[Given("I have entered (.*) into the calculator")]
public void GivenIHaveEnteredSomethingIntoTheCalculator(int number)
{
  //TODO: implement arrange (recondition) logic
  // For storing and retrieving scenario-specific data,
  // the instance fields of the class or the
  //     ScenarioContext.Current
  // collection can be used.
  // To use the multiline text or the table argument of the scenario,
  // additional string/Table parameters can be defined on the step definition
  // method.

  ScenarioContext.Current.Pending();
}

Мы видим, что на нашем калькуляторе печатают некоторые числа, ок, подтвердим это:

[Given("I have entered (.*) into the calculator")]
public void GivenIHaveEnteredSomethingIntoTheCalculator(int number)
{
    Calculator.Input(number);

}

И не стесняйтесь того, что пока Visual Studio не совсем понимает, что такое Calculator 😉

Отлично, смотрим на следующий метод, его мы тоже немного поправим:

[When("I press add")]
public void WhenIPressAdd()
{

    Calculator.Add();

}

И, вслед за ним, удостоверимся, что получаем именно то, чего желаем:

[Then("the result should be (.*) on the screen")]
public void ThenTheResultShouldBe(int result)
{
    Assert.IsEqual(result, Calculator.Result);
}

Теперь самое время разобраться с нашим калькулятором.

Создадим следующее поле:

protected Calculator Calculator = new Calculator();

Теперь пора бы разобраться и с типом Calculator, его мы создадим в проекте Calculator. Так же добавим reference из проекта Calculator.Tests на проект Calculator. С типом так же всё в порядке, однако не хватает некоторых методов, их тоже следует создать.

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

image

Теперь можно заняться имплементацией нашего калькулятора. На данный момент я предлагаю следующее:

public class Calculator
{
  private IList<int> _buffer = new List<int>();

  public int Result { get; private set; }

  public void Input(int number)
  {
    _buffer.Add(number);
  }

  public void Add()
  {
    Result = _buffer.Sum();
    _buffer.Clear();
  }

}

Запускаем наши тесты и видим, что они проходят, супер!

image

Вроде бы с первой фичей мы успешно разобрались, почему бы не взяться за вторую?

Второй возможностью будет деление:

http://github.com/aslakhellesoy/cucumber/blob/master/examples/i18n/en/features/division.feature

# language: en

Feature: Division

  In order to avoid silly mistakes

  Cashiers must be able to calculate a fraction

  Scenario: Regular numbers

    Given I have entered 3 into the calculator

    And I have entered 2 into the calculator

    When I press divide

    Then the result should be 1.5 on the screen

*в оригинале для cucumber у aslakhellesoy несколько иной синтаксис, но он на данный момент не поддерживается в SpecFlow

данную фичу мы добавим в наш тестовый проект под названием devision.feature. как не трудно заметить, что основные шаги мы с вами уже описали в предыдущем примере, сейчас осталось только добавить 1 метод:

[When("I press divide")]
public void WhenIPressDevide()
{
  Calculator.Devide();
}

Добавить так же метод в класс Calculator.

Скомпилировать наш проект. И запустить тесты. Конечно же они не пройдут, т.к. деление мы с вами ещё не реализовали:

public void Devide()
{
  Result = _buffer[0] / _buffer[1];
  _buffer.Clear();
}

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

Материалы

Исходный код рассматриваемого проекта http://dl.dropbox.com/u/4070949/Calculator.zip

Скринкаст от создателей SpecFlow http://www.specflow.org/specflow/screencast.aspx

Моя статья Пример практики BDD при работе со Specter Framework

Книга The RSpec Book: Behaviour Driven Development with RSpec, Cucumber, and Friends

Википедия http://en.wikipedia.org/wiki/Behavior_Driven_Development


Unit-тесты для SharePoint

Снова о unit-tests?


Да, я уже рассказывал о том, как “правильно” писать юнит-тесты для SharePoint, однако сегодня наткнулся на одну интересную разработку, речь о которой пойдёт далее.

В состав Pex входит инструментарий для создания stub-объектов под названием Mole, он умеет работать с закрытыми для наследования объектами, на коих строится объектная модель SharePoint.

Если вы не знаете что такое Pex

В компании Microsoft есть подразделение, занимающееся научными разработками, называется оно Microsoft Research, подробнее узнать о том, чем же занимается это подразделение можно узнать здесь: http://research.microsoft.com/en-us/research/default.aspx

Одно из разработок данного подразделения – средство white-box тестирования для .NET под названием Pex. Разработка доступна для скачивания: http://research.microsoft.com/en-us/downloads/d2279651-851f-4d7a-bf05-16fd7eb26559/.

Конечно же концептуально Pex противоречит некоторым аспектам юнит-тестирования, т.к. проверяет как написан код, а не что он должен делать.

Насколько известно для SharePoint удобно писать лишь интеграционные тесты, которые помогут вам убедится, в том, что решение благополучно “прилунилось” и проверить, что вся необходимая логика исполняется на “живых” объектах SharePoint.

Однако во многих случаях хочется избавиться от всех инфраструктурных накладок интеграционного тестирования (производительность: я не хочу уходить пить кофе каждый раз при запуске тестов, обновление данных и проч.) и проводить полноценное модуль тестирование в изолированной среде, что обычно и позволяют делать многочисленные фреймворки для создания “тестовых двойников”, примеры для .NET:

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

Пример работы с Mole

Предположим нам необходимо протестировать некоторый участок кода:

public void UpdateTitle(SPItemEventProperties properties) {
    using (SPWeb web = new SPSite(properties.WebUrl).OpenWeb()) {
        SPList list = web.Lists[properties.ListId];
        SPListItem item = list.GetItemById(properties.ListItemId);
        item["Title"] = item["ContentType"];
        item.SystemUpdate(false);
    }
}

Для этого нам необходимо будет произвести ряд действий для генерации тестового проекта, которые я опущу (они подробно описаны в литературе на которую я ссылаюсь). Больше всего интересует код, который будет заниматься э(mole)яцией типов SharePoint:

string url = "http://foo";
// с помощью рефикса MS мы подскажем исполняющей среде, что данный тип э(mole)ирует // SPItemEventProperties и что по вызову свойства WebUrl
// (заметьте что Get свойства опять же опрделен специфичностью синтаксиса) необходимо // будет вернуть заданную нами строку
var properties = new MSPItemEventProperties {
    WebUrlGet = () => url
};

Аналогично и для остальных объектов:

MSPSite.ConstructorString = (site, _url) => {
    new MSPSite(site);
};

new MSPSite(site) {
    OpenWeb = () => new MSPWeb()
};

Так будет выглядеть пример Assert’ов

ListsGet = () => new MSPListCollection {
    ItemGetGuid = id => {
        Assert.IsTrue(listId == id);
        return new MSPList();
    }
}

Думаю данных примеров будет достаточно, для того, чтобы понять, что достойной альтернативы TypeMock Isolator пока нет, однако, с точки зрения сравнения подходов к решению проблемы с промышленной и академической стороны Mole очень интересен.

Материалы

Статья об использовании Mole: http://research.microsoft.com/en-us/projects/stubs/stubstutorial.pdf

Статья о тестировании SharePoint с помощью Mole и Pex: http://research.microsoft.com/en-us/projects/pex/pexsharepoint.pdf


.NET ft. YAML

Введение

FileDownload[1] Разработчики нередко встречаются с проблемами конфигурации приложений, обычно для этих целей используется XML, в данной статье я хочу обратить внимание на более дружелюбный язык разметки.

YAML (YAML Ain’t Markup Language) – язык (существует с 2001 года) для серилизации данных, ориентированный на чтение и редактирование человеком типичных структур данных. Язык, как нетрудно догадаться, межплатформенный. Известен, в большинстве своём, использованием в Ruby On Rails в качестве средства конфигурирования. Подробнее на русском здесь: http://ru.wikipedia.org/wiki/Yaml

Пример YAML

%Пример

YAML: YAML не язык разметки

Что это: YAML — это человекоориентированный стандарт серилизации данных для всех языков программирования

Проекты:
  .NET:
  — yaml-net
  — yatools.net

.NET

Использовать данный язык можно и на .NET, для этого существуют:

  1. Visual Studio YAML Editor http://yaml.codeplex.com/
  2. YamlSerializer for .NET http://yamlserializer.codeplex.com/
  3. Yaml Library for .NET http://yaml-net-parser.sourceforge.net/default.html

Материалы

Официальный сайт проекта http://yaml.org

Yaml за 5 минут http://yaml.kwiki.org/index.cgi?YamlInFiveMinutesMinuteOne

Неплохая статья с примером использования http://www.codeproject.com/KB/recipes/yamlparser.aspx


Как подружить ASP.NET Controls и DI-контейнер

Интро

В последнее время решил немного освежить свои знания в ASP.NET, в связи с чем углубился в процессы генерации кода контролов по разметке (*.ascx, *.aspx) и обнаружил что можно делать очень интересные решения, о которых  о хочу поведать. Итак сегодня мы узнаем, как подружить наш Dependency Injection контейнер с генерируемым контролами кодом.

Поехали

DependencyInjection_Solution[1]

В качестве DI-контейнера будет выступать Microsoft Unity, но это не принципиально, всё что будет касаться DI не зависит от используемого контейнера.

Проблема состоит в следующем – есть некоторый ASP.NET Control, в который мы хотим внедрит зависимости, а так же воспользоваться услугами Service Locator’а для управления интересующими нас зависимостями.

В Microsoft Unity есть некоторые средства для того, чтобы сделать это не прилагая особенных усилий: мы можем произвести инъекцию в свойство элемента управления, нас интересующее примерно следующим образом:

  1. Отметить атрибутом Dependency необходимое свойство
    public class MyControl : UserControl
    {
            [Dependency]
            public MyPresenter Presenter
            {
                get { return _presenter; }
                set
                {
                    _presenter = value;
                    _presenter.View = this;
                }
            }
    }
  2. Проинициализировать элемент управления можно следующим образом

    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);
        _сontainer.BuildUp(GetType(), this);
    } 
  3. Позаботиться о местоположении контейнера в вашем приложении, я предлагаю использовать для этого HttpApplication, унаследовавшись от которого и произведя небольшие модификации файла global.asax мы получаем необходимое нам хранилище для контейнера, обращаться с ним необходимо примерно следующим образом

    ((Sapphire.Application)HttpContext.Current.ApplicationInstance).Container

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

Т.е. наш интерес состоит в том, чтобы класс MyUserControl выглядел примерно так (думаю сборщику страницы это не совсем понравится)

public class MyControl : UserControl
{
    public MyControl(MyPresenter presenter)
    {
         _presenter = presenter;
         _presenter.View = this;
    }
}

Предлагаю этим и заняться. Начнём с того, что у элементов управления, описанных в разметке страницы, при генерации страницы указываются их конструкторы без параметров, интересно, как можно управлять данным процессом, первоначально, покопавшись в web.config я предполагал сделать это через:

<buildProviders>
    <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 мы можем указать свою логику сборки элемента управления из разметки, это будет выглядеть примерно так

[ControlBuilder(typeof(MyControlBuilder))]
public class UserControl : System.Web.UI.UserControl
{
}

Теперь разберемся с реализацией  MyControlBuilder, этот тип должен наследовать от ControlBuilder и с помощью перегрузки ProcessGeneratedCode мы с вами сможем указать сборщику на необходимость использования нашего кода вместо вызова конструктора без атрибутов элемента управления:

    public override void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit,
                                              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

    private void ReplaceConstructorWithContainerResolveMethod(CodeMemberMethod buildMethod)
    {
      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 class PageExtensions
  {
    public static UserControl LoadAndBuildUpControl(this Page page, string virtualPath)
    {
      var control = page.LoadControl(virtualPath);
      return SapphireControlBuilder.Build<UserControl>(control.GetType());
    }
  }

Вот мы и справились с поставленной задачей, однако это ещё не всё. А почему теперь не воспользоваться всеми преимуществами Unity, и не внедрить в наш элемент управления AOP времени исполнения с помощью Unity Interception.

К примеру мы можем сделать следующее

public class MyControl : UserControl
{
    [HandleException]
    public override void DataBind()
    {
      base.DataBind();
    }
}

Это будет означать, что обработка исключений должна добавляться на лету, к тому ж предоставляя нам возможность её изменения во время исполнения, для начала пусть её реализация будет примерно следующая

  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
  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 { getset}
  }

Ну и конечно же надо сконфигурировать контейнер для создания наших прокси-обработчиков

    public static T Build<T>()
    {
      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

logoСегодня я расскажу о прототипе первого компонента под ярлычком Sapphire. Это REPL WebPart. Эта веб-часть предназначенная для производства оперативных изменений на серверной стороне SharePoint, так же для удаленного исполнения скриптов и тестирования некоторых кусков кода.

PreBody

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

Здесь есть небольшая презетнация, в которой я постарался отобразить принципы работы Repl WebPart:

В добавок к слайдам расскажу о том, что веб-часть представляет собой классический хостинг Dynamic Languages Runtime языков, пока из которых доступен только Python.

Body

Далее хотелось поговорить о том, как же всё это у меня получилось, итак проект включает в себе несколько основных модулей:

  • Веб-часть и контролы представления
  • Хостинг языков

Далее о них поподробнее

Language Hosting

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

  public interface ILanguagesFactory
  {
    ILanguage Create(string name);
  }

  public interface ILanguage
  {
    string Name { get}

    object Execute(string input);

    void SetVar(string name, object value);
    
    object GetVar(string name);
  }

Отлично, далее к нашему проекту присоединяются сборки, необходимые для имплементации скриптовых языков:

Всё готово для того, чтобы реализовать Python, как среду для исполнения кода, отлично, приступим:

  public class PythonLanguage : ILanguage
  {
    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, выглядеть это должно примерно так:

    [Test]
    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(string).Assembly);
      _scope.Engine.Runtime.LoadAssembly(typeof(Uri).Assembly);
      _scope.Engine.Runtime.LoadAssembly(typeof(SPList).Assembly); 

Это будет основой нашей работы с динамическими языками

WebPart + WebControls

Создадим проект с помощью SPVisualDev, добавим в него feature, внутри которой создадим веб-часть Repl WebPart, всё остальные действия стандартные, однако есть небольшой нюанс, нам нужен будет объект, с помощью которого можно будет выводить строковые значения после исполнения. Я решил создать для этих целей объект с классическим названием Console, его реализация предельна проста:

  public class 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

Интро

clip_image001

В данной статье я хочу произвести обзор руководства под названием “SharePoint Guidance” от подразделения Microsoft patterns & practices. Данное руководство предназначено разработчикам/архитекторам SharePoint, в нём описаны основные принципы построения систем на данной платформе. Над руководством трудились выдающиеся представители разработки под SharePoint, а так же он упорно держится в списке самых активных проектов на CodePlex. Далее чуть подробнее.

Описание руководства

Руководство фактически состоит из нескольких аспектов:

  1. SharePoint Guidance Library – библиотека наиболее используемых и полезных функций, таких как управление конфигурацией, абстрагирование слоя данных, логирование событий и сервисная инфраструктура
  2. Документация, в которой подробно описаны все принципы построения приведенных примеров, а так же руководства по основным вопросам, возникающим в разработке на SharePoint.
  3. Contoso Partner Portal Reference Implementation – показательное приложение на MOSS некоей компании Contoso, являющее собой экстранет-портал, в приложении используются практики наиболее приближенные к промышленным решениям.
  4. Contoso Training Management Reference Implementation – простое приложение HR-отдела, демонстрирующее базовые принципы построения решений на WSS.
  5. QuickStarts – два небольших примера самых простых приложений на SharePoint, а так же доступа к данным

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

image

Обзор руководства

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

Критика

logo Данное руководство содержит в себе большое количество практичной полезной информации, однако в некоторых технических решениях моя точка зрения расходится с его создателями, в связи с чем мною был начат проект Sapphire: SharePoint Application Framework, который будет так же являться каркасом для построения решений на SharePoint, а так же включать примеры решений и необходимые в повседневной разработке компоненты.

Ресурсы по SharePoint Guidance

Скачать полную версию за август 2009 года можно здесь:

SharePoint Guidance так же представлен на CodePlex: http://www.codeplex.com/spg/ здесь можно задать интересующие вопросы и скачать все последние изменения.

Так же некоторые главы из SharePoint Guidance представлены в видеоряде на channel 9:

Ресурсы по 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, это связанно с некоторыми особенностями ресурса, однако поправимо.

Данными работами я и предлагаю заняться, для этого нам понадобятся:

После того, как мы скачали исходники, стоит разыскать скрипт под именем pygments_package\devhawk_formatter.py и немного его подкоректировать:

            # a style item is a tuple in the following form:
            # colors are readily specified in hex: ’RRGGBB’
            if style[‘color’]:
                start += ‘<span style=»color:#%s«>’ % style[‘color’]
                end = ‘</span>’ + end

заменить на

            # a style item is a tuple in the following form:
            # 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). Объекты реального мира, нашедшие отражение в нашем приложении частенько так же передают достаточную сложность, следовательно, достаточно корректно построенная модель крайне тяжело поддаётся перемещению между слоями приложения (не путать с легкостью вносимых изменений).

Data Transfer Object Тем не менее достаточно часто появляется необходимость “распределения” (я имею ввиду создание промежуточных сущностей, а не растекание модели по слоям) модели между слоями, для отображения, к примеру, атрибутов её сущностей пользователям (в шаблонах представления MVx), а так же передаче по сервисам (Data Transfer Object). Порой бывает даже, что модель “распределяется” для тестирования некоторых аспектов.

Предположим, мы в Африке, у нас банановая плантация, всё классно, выращиваем, продаём, выращиваем, продаём, но тут внезапно внутренний рынок переполняется и нам надо расширятся (к примеру вести бананы в Россию), мы пишем WCF сервис, который будет слать наши бананы. Так как бананы в Африке имеют несколько иное значение, чем в России, то, соответственно нам понадобятся лишь некоторые атрибуты (остальные фактически не имеют значения), которые мы забубеним в наш DTO

Правильнее было бы дать классу BananaWrapper название BananaDTO, для того, чтобы точно отображать его функциональное назначение, но я оставлю название таким для большего уровня абстракции, к примеру, если нам понадобится сделать автомат по продажи бананов и поместить этот объект в Presenter Model

Хочу заметить, что порой задача преобразования объектов становится довольно-таки нетривиальной и в лучшем случаем выглядит примерно подобным образом (это решение в лоб, есть ещё более изощренные методы ;)):

  1. public class Banana
  2. {
  3. public string Country { get; set; }
  4. public double Price { get; set; }
  5. public int Age { get; set; }
  6. }
  7. public class BananaWrapper
  8. {
  9. public string Country { get; set; }
  10. public double Price { get; set; }
  11. public int Age { get; set; }
  12. }
  13. public class BananaMapper
  14. {
  15. public BananaWrapper GetWrapper(Banana banana)
  16. {
  17. return new BananaWrapper
  18. {
  19. Country = banana.Country,
  20. Price = banana.Price,
  21. Age = banana.Age
  22. };
  23. }
  24. }

* This source code was highlighted with Source Code Highlighter.

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

AutoMapper

И тут на сцену выходит наш персонаж – AutoMapper, и сразу же говорит мне: — послушай, ты что такое пишешь? тебе не лень? ты не боишься допустить ошибок? хочешь я тебе помогу?!. Я конечно же соглашаюсь, и получаю в ответ следующее решение моей проблемы:

  1. public class BananaMapper
  2. {
  3. public BananaMapper()
  4. {
  5. Mapper.CreateMap<Banana, BananaWrapper>();
  6. }
  7. public BananaWrapper GetWrapper(Banana banana)
  8. {
  9. return Mapper.Map<Banana, BananaWrapper>(banana); ;
  10. }
  11. }

* This source code was highlighted with Source Code Highlighter.

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

Итак, что же за механизмы лежат внутри AutoMapper?

AutoMapper проверяет есть соответствующие поля в указанных типах, соответствие проводится как по имени свойства, так и по его типу. Даже такие нюансы, как Product.Name и ProductName будут учтены и обработаны автоматически (wow!). Плюс ко всему методы GetXXX() будут ложится на свойства XXX (да, ну и естественно для особо раздражительных все эти прелести можно отключить и переопределить всё в своих собственных таблицах соответствия (далее мапках)).

Кастомная конфигурация выглядит примерно следующим образом:

  1. Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
  2. .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
  3. .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour))
  4. .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute));

* This source code was highlighted with Source Code Highlighter.

Кстати, все ваши кастомные конфигурации легко поддаются проверке с помощью следующего метода:

  1. Mapper.AssertConfigurationIsValid();

* This source code was highlighted with Source Code Highlighter.

Так же не плохо работает с:

История

Проект появился в конце’08-начале’09, около полугода находился в версии 0.31, сейчас же добрался до RC 1.0, думаю, что релиз уже совсем скоро.

Overhead?

banana Дебаты по поводу того, насколько быстрее будет работать 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.