Generate SharePoint project constants with T4 by feature definitions

Problem

Usually the main problem of SharePoint development is accessing SPListItem fields values by theirs internal names of field ids. For example:


SPListItem listItem = GetLIstItem();
// I prefer to use field ids for speed and garanteed way extract values
listItem[SPBuiltInFieldId.Title]

The output file will be like that:


But in some scenarios fieldId doesn’t help me for get values, such as get it in item event receivers. In this case I need to know the field internal name.


public override void ItemAdding(SPItemEventProperties properties)
        {
            string toolTipFieldInternalName = "";
            //oops, this place have some perfomance overhead
            using (SPWeb web = properties.OpenWeb())
            {
                toolTipFieldInternalName = web.Lists[properties.ListId].Fields[toolTipFieldId].InternalName;
 
            }
 
            string urlVal = properties.AfterProperties["URL"].ToString();
            SPFieldUrlValue val = new SPFieldUrlValue(urlVal);
            string desc = val.Description;
            properties.AfterProperties[toolTipFieldInternalName] = desc;
        }

For best experience you may install Visual T4 Editor

There is a simple way to extract and generate usefull constants for field ids and internal names from features definition files in your project with T4 mechanism built-in into visual studio:

Constants.tt (this code works fine only in Visual Studio 2010)

<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#" debug="true" hostSpecific="true" #>
<#@ assembly name="Microsoft.VisualStudio.TextTemplating.Modeling.10.0.dll"#>

<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ Assembly Name="System.Xml.dll" #>
<#@ Assembly Name="System.Xml.Linq.dll" #>
<#@ Assembly Name="System.Windows.Forms.dll" #>
<#@ Assembly Name="EnvDTE" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #> 
<#@ import namespace="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<# 
var currentDirectory = Path.GetDirectoryName(Host.TemplateFile);
var xmlFiles = Directory.GetFiles(currentDirectory, "*.xml", SearchOption.AllDirectories);
var fieldQualifier = XName.Get("Field", "http://schemas.microsoft.com/sharepoint/");
var listdQualifier = XName.Get("ListInstance", "http://schemas.microsoft.com/sharepoint/");
#>using System;	

namespace <#= System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint") #>
{
    public static partial class Constants 
    {
        public static class Fields
        {
<#  
		var fields = xmlFiles
					.Select(XDocument.Load)
					.Select(d => d.Descendants(fieldQualifier))
					.SelectMany(x => x)
					.Where(i => i != null)
					.Select(i => new KeyValuePair<string, string>(
							i.Attribute("ID") != null ? i.Attribute("ID").Value : null,
							i.Attribute("Name") != null ? i.Attribute("Name").Value : null))
					.Where(i => !string.IsNullOrEmpty(i.Key) && !string.IsNullOrEmpty(i.Value))
					.Distinct();
            foreach (var field in fields)
            {
			#>
            public static string <#= field.Value #>_InternalName = "<#= field.Value #>";
            public static Guid <#= field.Value #>_Id = new Guid("<#= field.Key #>");
			
<#
			}
		   #>

		}

        public static class Lists
        {
		}
    }
} 

The output file will be like follow:



 
using System;	

namespace MyProjectNameSpace.Project1
{
    public static partial class Constants 
    {
        public static class Fields
        {
            public static string DocKind_InternalName = "DocKind";
            public static Guid DocKind_Id = new Guid("{b4fc3e5e-7573-4e39-9417-81d85f25ed73}");
			
            public static string RegDate_InternalName = "RegDate";
            public static Guid RegDate_Id = new Guid("{e453a0ee-7797-4d90-941c-0ea9a775f48d}");
			
            public static string RegUser_InternalName = "RegUser";
            public static Guid RegUser_Id = new Guid("{616997f5-6cf2-4795-b625-d7b7f4681a2e}");
        }

       public static class Lists
       {
       }
    }
} 

Пишем клиента для 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/


Презентации с выступлений на прошлой неделе

На прошлой неделе состоялось 2 очень значимых для меня мероприятия:

На обоих мероприятиях мне посчастливилось выступать. Очень доволен как организацией, так и новыми знакомствами. Если кому-то интересно, выкладываю здесь свои материалы (пока только слайды, чуть позже будут ещё и видео) с обоих выступлений.





SharePoint and oData

В SharePoint 2010 многое сделано для открытости доступа к данным, в том числе поддержка oData. Клиентская модель построена поверх этих возможностей.

Для того, чтобы опробовать возможности SharePoint oData в действии вам достаточно вызвать сервис ListData:

http://[serverName]/_vti_bin/ListData.svc

Если в результате получите ошибку 404, то скорее всего у вас не стоит ADO.NET Data Services v1.5 CTP2

Далее можно попробовать воспользоваться Visual Studio

image

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

  class Program
  {
    static void Main(string[] args)
    {
      var context = new ClientDataContext(new Uri("http://localhost/_vti_bin/ListData.svc"));
      context.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
      var результат = context.ДокументыПроекта.Где(x => x.Имя != null).ToList();
    }
  }

  public static class Расширения
  {
    public static IEnumerable<T> Где<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
      return source.Where(predicate);
    }
  }

Ну или просто возможность работы из браузера:

image

А так же из Excel, благодаря PowerPivot.


SharePoint Business Connectivity Services: Новые возможности

Записал скринкаст по Business Connectivity Services.

  • Принципы построения интеграционных решений на SharePoint
  • Работа с внешними данными
  • Демонстрация возможностей SharePoint BCS.
  • Обсуждение принципов работы, дистрибуции и потенциала платформы.
  • Демонстрация примеров работы с сервисом.

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

Domain-Driven Design и CQRS

Ведущий: butaji

Наши гости: frozen_space и chaliy

Содержание:

  • Что значат все эти буквы, стоит ли изучать?
  • Список литературы

Applying Domain-Driven Design and Patterns: With Examples in C# and .NET http://www.amazon.com/Applying-Domain-Driven-Design-Patterns-Examples/dp/0321268202

Yahoo group Domain-Driven Design http://tech.groups.yahoo.com/group/domaindrivendesign/

http://www.infoq.com/minibooks/domain-driven-design-quickly

http://www.domaindrivendesign.org/

CQRS à la Greg Young http://blog.fohjin.com/blog/2009/11/12/CQRS_a_la_Greg_Young

  • Насколько DDD реально имеет место в проектах?
  • Действительно ли DDD помогает управлять сложностью?
  • Сколько паттернов вы запомнили из книги Ивенса?
  • CQRS — недостаAтки и преимущества
  • OpenSource примеры

DDDSample http://dddsample.sourceforge.net/

S#arp Architecture http://code.google.com/p/sharp-architecture/

http://www.codeplex.com/dddpds

http://www.habanerolabs.com/

RSS-поток подкастов


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

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

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

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

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

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

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

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

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


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

SOLID & TDD и прочие показатели качества кода

Ведущие: dnesteruk & butaji

Наши гости: Alexander Byndyu и Vitaly Stakhov

SCRUM & XP

OOD: Best practicies

SOLID

  • Single Responsibility Principle
  • Open/Closed Principles
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

TDD: Best Practicies

Ещё говорили о

Pain-Driven Development

Spolsky vs Uncle Bob

NHibernate Sources, NAnt

 

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

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

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


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

Всё, что вы хотели узнать о ASP.NET

Участники: dnesteruk & butaji

Что нового в ASP.NET MVC2.

  • Асинхронные контроллеры
  • Зоны (areas)
  • Валидация через data annotation
  • Строго-типизированные хелперы
  • Хелпер-темплейты, параметры по-умолчанию

Альтернативные view engines

Удобен ли Visual Studio для веб-разработки

FubuMVC

  • Новое понимание MVC. Другой контроллер. Конвенции.

Почему я уйду на Ruby on Rails / Django

Как хорошо работать с Asp.Net AJAX и jQuery

HTML5 + CSS3 (@font-face)

RIA

Создавать ли View для iPhone

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

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

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


Альтернативный MVC Framework на ASP.NET: FubuMVC

Введение

Почему же альтернативный? Всё просто, данный framework пишется силами выдающихся участников ALT.NET community, а именно:

Jeremy Miller, Chad Myers, Mark Nijhof, Ryan Kelley, и Joshua Flanagan.

FubuMVC не имеет никакого отношения к ASP.NET MVC Framework, кроме того, что так же построен поверх ASP.NET и реализует паттерн Front-Controller. Название FubuMVC строится из акронима “For us by us” и аббривеатуры MVC (кто ещё не знает, что это значит Model-View-Controller ?). Проект был инициирован по причине недовольства вышеперечисленных личностей архитектурными решениями, принятыми в ASP.NET MVC. Сказать, по правде, по прошествии года, после старта проекта, он не утратил своей элегантности и простоты в подходах.

FubuMVC вносит несколько непривычное понимание Controller’а. Теперь это не просто некоторый класс, теперь это больше логическая связность некоторых методов. Это раскрывает более широкие возможности для настройки специфичного поведения Controller’а, к примеру, обработки ошибок, реализации правил кэширования, определения типа возвращаемых значений. К тому же возможность динамического объединения вызовов Action’ов уже является удобным механизмом к расширению. Далее мы более подробно поговорим об особенностях этой реализации в FubuMVC.

В качестве View Engine (в данном случае является лишь определением и не имеет отношения к ASP.NET MVC View Engine) используется механизм рендринга WebForms (проще говоря FubuPage, которая является View в FubuMVC, наследует от System.Web.UI.Page), но имеется возможность в подключении всех существующих в ASP.NET MVC ViewEngine’ов, примером тому может быть реализация SparkViewEngine.

Конечно же уместным будет вопрос, а зачем же нужен “другой” MVC Framework, с меньшим количеством участников проекта, с меньшим вниманием со стороны разработчиков, с меньшим количеством тестеров и прочими атрибутами mainstream проектов.

В основу проекта FubuMVC его создатели заложили больше возможностей для кастомизации проекта под прикладные нужды, а так же конвенциям (соглашениям) относительно настройки и расширяемости. Причём конвенции должны покрывать основные и типичные нужды приложения, коих в разработки решений достаточно большое количество. Какие-либо дополнительные разработки будут инициировать собой появление дополнительных модулей.

ScreencastsАналогичные конвенции (соглашения) существую в известном всем MVC Framework на Ruby: Ruby On Rails. На своей практике разработки под Ruby On Rails я неоднократно убеждался, что данные конвенции действительно позволяют снизить сложность и количество написанного кода. Примером тому может служить ORM Active Record с его соглашениями касательно преобразования объектов в таблицы реляционной базы данных, а так же соглашение, относительно хранения конфигурации в формате YAML.

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

FubuMVC на GitHub: http://github.com/darthfubumvc/fubumvc

Первое погружение

Начнём с конвенций именования и структуры проекта. Вся работа с настройкой приложения FubuMVC ведётся через некоторый DSL, стиль конфигурирования очень близок к работе с FluentNhibernate.

public class HelloWorldFubuRegistry : FubuRegistry
{
    public HelloWorldFubuRegistry()
    {
        IncludeDiagnostics(true);

        Applies.ToThisAssembly();

        Actions
            .IncludeTypesNamed(x => x.EndsWith("Controller"));

        Routes
            .IgnoreControllerNamespaceEntirely()
            .ConstrainToHttpMethod(action => action.Method.Name.EndsWith("Command"), "POST")
            .ConstrainToHttpMethod(action => action.Method.Name.StartsWith("Query"), "GET");

        Views
            .TryToAttach(x=>
            {
                x.to_spark_view_by_action_namespace_and_name(GetType().Namespace);
                x.by_ViewModel_and_Namespace_and_MethodName();
                x.by_ViewModel_and_Namespace();
                x.by_ViewModel();
            });

        Output.ToJson.WhenCallMatches(action => action.Returns<AjaxResponse>());

        HomeIs<HomeInputModel>();
    }
}

Исходя из данной конфигурации IoC-container’а нетрудно догадаться о следующем:

  • Все классы, оканчивающиеся на “Controller” будут зарегистрированны в качестве Controller
  • Все запросы, оканчивающиеся на “Command” должны будут приниматься POST’ом, а начинающиеся на “Query” GET’ом
  • Все View будут заполняться соответствующими по именованию ViewModel
  • Весь вывод, возвращающий тип AjaxResponse будет серилизоваться в JSON

В качестве IoC-container’а в FubuMVC выбран StructureMap. Выбор не случаен, т.к. фактически StructureMap является старейшим IoC/DI-container’ом. Начало разработки датировано июнем 2004ого года и велось Jeremy D. Miller, так же и участником проекта FubuMVC.

Второе погружение

Давайте рассмотрим несколько примеров приложений на FubuMVC

Первое из них http://github.com/DarthFubuMVC/fubumvc-examples/tree/master/src/Actions/ControllerActionStyle/

Начнём, с того, что проверим его работоспособность:

image

В namespace’е SimpleWebsite.Core определена наша модель предметной области, а именно Movies, работе с которыми и просвещенно приложение.

В SimpleWebsite.Controllers определен единственный Controller и View, работу которых мы и видим на рисунке выше.

Давайте разберемся с MoviesController. Судя по его конструктору нетрудно догадаться, что DependencyInjection мы получаем out-of-box без каких-либо переопределений Controller Factory:


private readonly IRepository _repository;

public MoviesController(IRepository repository)
{
    _repository = repository;
}

Метод List() является “списочным” и предоставляет нам доступ к ViewModel


public ListMoviesViewModel List()
{
    return new ListMoviesViewModel {Movies = _repository.Query<Movie>()};
}

Стоит обратить внимание на конвенцию именования в данном случае. Под ViewModel (читается так же как PresentationModel) понимается адаптированная для представления модель, что подчеркивает SoC в реализации FubuMVC. View в данном случае будет строго типизировано на  этот тип:

 public class List : FubuPage&lt;ListMoviesViewModel&gt;   <br />{}

Метод Add() попадает под правило обработки AjaxResponse, следовательно вернется на страницу в виде JSON, где будет радужно встречен jQuery обработчиком.

image

Аналогичная ситуация с методом Remove().

Стоит обратить внимание на конвенцию именования принимаемых данными методами типов:

AddMovieInput, RemoveMovieInput

Наименования данных классов указывают на направление данных, т.е. их ввод с запросом. С помощью этих типов на View мы определяем Url для обработки соответствующих Action’ов Controller’а:

<%= Get<IUrlRegistry>().UrlFor(new AddMovieInput()) %>

FubuMVC реализует 3 модели обработки запросов:

  1. Controller / Action Привычный для ASP.NET MVC Framework способ. Определяется некоторый тип — Controller, который содержит в себе соответствующие методы — Action’ы. Данный пример был рассмотрен выше.
  2. Controller-less Actions (HandlerStyle) Данный способ подразумевает под собой “обезличенные” Action’ы. т.е. без привязки к какому-либо Controller’у. Правила нахождения Action’ов описываются Policy, которые можно описывать и добавлять в коде.
  3. REST-like (EndPointStyle) Этот способ близок к предыдущему, однако, в отличии от него реализует не только метод Execute, а все методы, необходимые для REST взаимодействия, каждый метод Get(), Post(), Put(), Head() в дальнейшем будет соответствовать типу HTTP запроса:
var httpVerbs = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
    {"GET", "POST", "PUT", "HEAD"};

Погружаемся далее

Как вы уже догадались, FubuMVC славится своими возможностями для расширения. Предлагаю ознакомиться с ещё одним примером: http://github.com/DarthFubuMVC/fubumvc-examples/tree/7c4b2165aedb6754a01538aee956e29d4060654c/src/Outputs/VaryByAcceptHeader

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

Этот пример практически ни чем не отличается от рассматриваемого, раннее, за тем лишь исключением, что в классе

VariableOutputConvention : IConfigurationAction

мы регистрируем интересующие нас возвращаемые типы, а так же указываем дополнительный Behavior для рендринга страницы:

RenderVariableOutput : BasicBehavior

После чего достаточно только заполнить интересные вам Movies http://localhost:59187/movies/list и попросить вернуть их в JSON http://localhost:59187/movies/list?renderformat=json или в XML http://localhost:59187/movies/list?renderformat=xml

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

Диагностика

По адресу http://localhost:59187/_fubu вас будет ждать небольшой сюрприз, а именно – диагностическая утилита для FubuMVC.image

C её помощью вы сможете просматривать все зарегистрированные в вашем приложении

  • Chains – цепочки поведения, так называются логические связи между маршрутами и Action’ами. О цепочках можно посмотреть все обертки и возвращаемые значения.
  • Routes – показывает все зарегистрированные маршруты, и связанные с ними Action’ы, тип выводимых данных и цепочки поведения.
  • Actions – зарегестрированные в приложении Action’ы, их маршруты и возвращаемые значения
  • Inputs – возможные типы и способы ввода в вашем приложении со стороны запросов

Заключение

При первом и поверхностном знакомстве проект FubuMVC не показался мне чем-то выдающимся. Однако в последнее время мой интерес к нему постоянно растёт. Я считаю, что FubuMVC является более удобным и гибким инструментом, по сравнению с ASP.NET MVC. Однако это ещё не значит, что я уже готов доверить данному проекту свои коммерческие проекты. Тот факт, что сообщество всё же поддерживает многообразие качественных решений для создания приложений на ASP.NET, может только радовать.

Материалы

FubuMVC Contrib http://code.google.com/p/fubumvc-contrib/

Front Controller http://msdn.microsoft.com/en-us/library/ms978723.aspx

FubuMVC — Front Controller style framework  http://blog.fohjin.com/blog/2009/2/21/FubuMVC_Front_Controller_style_framework

FubuMVC Google Group http://groups.google.com/group/fubumvc-devel?pli=1

Ещё один MVC Framework на ASP.NET OpenRasta http://trac.caffeine-it.com/openrasta

Соглашение относительно настройки http://msdn.microsoft.com/ru-ru/magazine/dd419655.aspx