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-поток)