Показаны сообщения с ярлыком .NET 3.5. Показать все сообщения
Показаны сообщения с ярлыком .NET 3.5. Показать все сообщения

воскресенье, 14 июня 2009 г.

Кодогенерация T4

Нет, это не про Терминатора :)
Это про использование T4 Text Template Transformation Toolkit, встроенного в Visual Studio 2008 для автоматической генерации кода.
Кратко задача – есть исходники. Опять-то таки сгенерированные, но другим инструментом, Thrift.
Код на C#, публичные поля и свойства в классах с одинаковым именем, только различаются регистром.
Код необходимо использовать из VB.NET. Упс! VB.NET нечувствителен к регистру! Приплыли.
Исходники конечно есть, но они регулярно обновляются – так что их исправлять нельзя.
Классы не помечены partial – расширить напрямую тоже нельзя.
Но у нас же есть extension методы – спасибо .NET 3.5! Можно понаписать методов (эх… пока только методы, эктеншен свойств нет) с названием совпадающим со свойствами, но с каким-нибудь префиксом, подчеркиванием например.
Ок, хорошо. Но вручную писать обертки на 20 классов?! Да они еще, как я сказал, могут обновиться в будущем. Тут нужна автоматизация… И в VS 2008 она уже встроена – движок кодогенерации T4.
Файлы с расширением tt. Синтаксис очень похож на ASP.NET, только исполняются внутри Visual Studio (и не только, хостом может выступать любое приложение, и ваше в том числе).
И вот всё волшебство:

<#@ template language="C#v3.5" hostspecific="true" #>
<#@ assembly name="EDAM.dll" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.CodeDom" #>
<#@ import namespace="System.CodeDom.Compiler" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="Evernote.EDAM.Type" #>

// Autogenerated by <#= Host.GetType() #>
// <#= DateTime.Now #>
// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING

namespace Evernote.EDAM.Type
{
<# foreach (var @class in Assembly.GetAssembly(BaseClass)
              .GetExportedTypes()
              .Where(type => !type.IsNested)
              .Where(type => type.Namespace == BaseClass.Namespace)) { #>
  public static class <#= @class.Name #>Ex
  {
  <# foreach (var prop in @class.GetProperties()) { #>
    public static <#= PrintType(prop.PropertyType) #> _<#= prop.Name #>(this <#= prop.DeclaringType #> value)
    {
      return value.<#= prop.Name #>;
    }
  <# }#>
  }
<# } #>
}
<#+ Type BaseClass = typeof(Note); #>
<#+
public string PrintType(Type type)
{
  var typeExpr = new CodeTypeReferenceExpression(type);
  var csProvider = Microsoft.CSharp.CSharpCodeProvider.CreateProvider("C#");
  var writer = new StringWriter();
  csProvider.GenerateCodeFromExpression(typeExpr, writer, new CodeGeneratorOptions());
  return writer.ToString();
} #>


Вкратце, по шагам:
  1. Перебираем все классы из сборки, из нужного пространства имен.
  2. Для каждого класса генерируем статический класс с таким-же именем и суффиксом Ex.
  3. Перебираем все свойства класса.
  4. Генерируем экстеншен-метод нужного типа с именем как оригинальное ствойство, но с префиксом _.
  5. Для генерация имени нужного типа используется маленькая хитрость. Так как по простому дженерики будут выводиться в IL-нотации, т.е. например System.Generic.List’1[System.String]. И это не будет компилироваться. Надо System.Generic.List<System.String> (для C#). Что и делается через CodeDom. Так как сборка на C# – то и провайдер для C# используется. Можно генерировать и в VB. Как в CodeDom, так и в самом T4 кстати.
Всё, теперь при сборке будет генерироваться набор расширений для каждого нужного класса с методами дублирующими все свойства, которые уже без проблем можно использовать в VB.NET. Кстати, в VB можно опускать скобки при вызове метода, если он не принимает параметров, что делает код еще более изящным (насколько вообще можно говорить об изящности в этой ситуации):

C# note.Attributes.Lattitude


VB.NET note._Attributes._Lattitude


Полезные ссылки:

пятница, 15 мая 2009 г.

Яндекс.Фотки API

Яндекс.Фотки наконец-то открыли официальный API. Базирован на AtomPub.
Решил перевести существующие решения для Яндекс.Фоток на него (Picasa, Архив, WLW, WLPG).

Для работы с AtomPub в .NET попробовал несколько вариантов:

  1. System.ServiceModel.Syndication из .NET 3.5
    Базовые сущности ServiceDocument, AtomFeed, AtomEntry.
    Но все изменяющие операции (Insert, Update, Delete) приходится делать врукопашную, через WebRequest.

  2. Microsoft.Web.AtomPub
    Небольшая надстройка над первым пунктом из Live Framework SDK. Негибкая, на некоторых операциях с сервером от Яндекса давится.

  3. AtomSite
    Как оказалось, это серверная имплементация AtomPub, не клиент.

  4. GData .NET Client Library
    Предназначается для работы с сервисами Гугла, но есть базовая библиотека для работы с AtomPub, непривязанная к особенностям Гугла. Немного перегружена, но довольна гибкая и в исходниках!



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


namespace Team23.YandexFotki
{
  public class YaDataRequest : GDataRequest
  {
    ...
  }
}

суббота, 20 декабря 2008 г.

Типизированный путь к действию контроллера в ASP.NET MVC

В ASP.NET MVC для генерации путей (url) для действий (action) контроллеров (controller) можно использовать методы-расширения объекта UrlHelper, такой как Url.Action(string actionName, string controllerName). Но этот метод использует нетипизированные параметры, просто имена action и controller, где легко можно ошибиться. А главный недостаток – нет поддержки рефакторинга, и если вы захотите переименовать действие контроллера – вам придется нудно и без гарантий от ошибки переименовывать их вручную.

Но если внимательно посмотреть – то можно найти замечательный класс – ExpressionHelper и его метод GetRouteValuesFromExpression, который по лямбде-выражению (expression) получит необходимые параметры роутинга. Потом, уже на основе этих параметров можно получить и путь. И конечно, с лямбда-выраженями полная поддержка рефакторинга (спасибо ReSharper за наше счастливое настоящее).

Все бы хорошо – но этот метод не принимает во внимание атрибут ActionName, с помощью которого можно изменить имя действия. Встроенная функция всегда возвращает имя метода, которое в 99% совпадает с именем действия, но не всегда.

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

   1: using System;


   2: using System.Linq;


   3: using System.Linq.Expressions;


   4: using System.Web.Mvc;


   5: using System.Web.Routing;


   6:  


   7: using Microsoft.Web.Mvc.Internal;


   8:  


   9: namespace MediaOnline.Web.Helpers


  10: {


  11:     public static class UrlExtensions


  12:     {


  13:         public static string Action<T>(this UrlHelper url, Expression<Action<T>> action) where T : Controller


  14:         {


  15:             return Action(url, null, action);


  16:         }


  17:  


  18:         public static string Action<T>(this UrlHelper url, string name, Expression<Action<T>> action) where T : Controller


  19:         {


  20:             return RouteTable.Routes.GetVirtualPath(url.RequestContext, name,


  21:                 GetRouteValuesFromExpression(action)).VirtualPath;


  22:         }


  23:  


  24:         public static RouteValueDictionary GetRouteValuesFromExpression<TController>(Expression<Action<TController>> action) where TController : Controller


  25:         {


  26:             var route = ExpressionHelper.GetRouteValuesFromExpression(action);


  27:  


  28:             var body = (MethodCallExpression)action.Body;


  29:             var customName = (body.Method.GetCustomAttributes(typeof (ActionNameAttribute), false))


  30:                 .Cast<ActionNameAttribute>().FirstOrDefault();


  31:  


  32:             if (customName != null)


  33:             {


  34:                 route["Action"] = customName.Name;


  35:             }


  36:             return route;


  37:         }


  38:     }


  39: }


вторник, 21 октября 2008 г.

Электронная таблица с помощью ANTLR

В рамках тестового задания для одной компании ;) мне  надо было реализовать простую электронную таблицу. Простую-то простую, но поддерживающую адресацию отдельных ячеек (A1), диапазонов (A1:Z1), вычисление как простых арифмитических операций ( +, –, *, /), так и функций (SUM, MIN, MAX) и выражений свертки (REDUCE). Последнее описывает действия выполняемые над диапазоном ячеек с использованием накопленного (агрегатного) и текущих значений, например REDUCE(A1:Z1, 1, X*Y) вычислит произведение всех ячеек из диапазона A1:Z1.

Но прежде чем вычислить что-то, надо понять что – т.е. распарсить текстовый вход и построить на основе него AST (Abstract Syntax Tree) – абстрактное синтаксическое дерево. Т.е. независимую от какого-то языка модель описывающую небоходимый набор выражений, и только потом спроецировать ее (реализовать) на конкретном языке программирования и вычислить. Или необязательно программирования, и необязательно вычислить – а например просто распечатать в структурированном виде.

Так вот, для этого незаменимым и эффективным инструментом оказался ANTLR. Универсальный генератор лексеров и парсеров. С помощью специального языка можно описать грамматику вашего языка (в моем случае формулы вичисления и адресации ячеек) и получить готовые исходники на вашем языке программирования (C#, Java, C и т.д.) для его лексического и синтаксичесго анализа.

Ну а после того как разобранное дерево выражений было готово – построить его вычисление было уже делом техники. В технику входило использование LINQ Expressions и лямбд из .NET 3.5 :).

пятница, 10 октября 2008 г.

GeoRSS и его использование на Virtual Earth и Google Maps

GeoRSS это еще один пример удачного расширения формата RSS (я уже рассказывал о других удачных расширениях). Этот формат позволяет встраивать информацию о географическом положении объектов, так называемый геокодинг. Многие сервисы уже начинают использовать эту информацию – и как поставщики, например FLickr выставляет данные о месте, где сделана фотография, если таковая имеется, и как потребители, например в Google Maps можно ввести адрес потока rss с геоданными и он их покажет. Или интересный сервис Panaramio, ототого же Гугла, и как поставщик и как потребитель GeoRSS, здесь можно посмотреть фотографии интересных мест (необязательно известных), сделанные самими пользователями.

Ну с теорией достаточно, теперь посмотрим как это можно сделать на своем сайте (конечно с помощью .NET :) ).

Во-первых, генерация RSS потока с нужными расширенями. Тут ничего нового. Как я описывал уже, с помощью SyndicationFeed и LINQ for XML это очень просто. Есть и готовые обертки для генерации GeoRSS данных.

Во-вторых, нам надо показывать эти данные у себя на сайте. Мне известны два популрных картограцических сервисов с API для встраивания карт у себя – Google Maps от Гугл и Virtual Earth от Майкрософт. Оба сервиса имеют функциональность показа GeoRSS потоков. Как не удивительно, но у Майкрософт с этим удобнее и больше контроля над покащываемыми данными.

На Google Maps достаточно добавить специальный слой и всё.

// The GGeoXml constructor takes a URL pointing to a KML or GeoRSS file.
// You add the GGeoXml object to the map as an overlay, and remove it as an overlay as well.
// The Maps API determines implicitly whether the file is a KML or GeoRSS file.

function initialize()
{
  if (GBrowserIsCompatible())
  {
    map = new GMap2(document.getElementById("map_canvas"));
    geoXml = new GGeoXml(http://mapgadgets.googlepages.com/cta.kml);
    map.addControl(new GLargeMapControl());
    map.addOverlay(geoXml);
  }
}

Но под “всё” имеено всё и заканчивается. Больше никакой информации мы не имеем, ни количество объектов, ни их расположение, ничего. Какие-то свойства мы может контролировать в самом RSS потоке, как цвета, иконки и т.д. Но если, например, мы используем внешний фид, с третего сайта – этого ничего нам недоступно.


На Virtual Earth мы также можем добавить специальный слой на карту

var veLayerSpec = new VELayerSpecification();
veLayerSpec.Type = VELayerType.GeoRSS;
veLayerSpec.ID = 'Hazards';
veLayerSpec.LayerSource = 'http://localhost/hazards/hazards.xml';
veLayerSpec.Method = 'get';
veLayerSpec.IconUrl = 'hazard.gif';
map.AddLayer(veLayerSpec);
 

или импортировать объекты из GeoRSS потока в существующий слой

var slGeoRSS= new VEShapeLayer();
var veLayerSpec = new VEShapeSourceSpecification(VEDataType.GeoRSS, url, slGeoRSS);
map.ImportShapeLayerData(veLayerSpec, null, false);
 

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


Еще смежной, но отдельной темой, с которой я столкнулся при работе над проектами моих заказчиков – это хранение и показ собственных географических данных на картах. В предыдущих примерах имелось ввиду что геоданные у нас имеются, причем в нужном формате для GeoRSS. Так вот, чтобы они действительно имелись, очень удобно оказалось использовать новый тип географических данных в Microsoft SQL Server 2008. Так называемый Spatial data type, куда входит geometry тип и geography тип. Но об этом как-нибудь в другой раз. :)

понедельник, 6 октября 2008 г.

Video Sitemap

Sitemap полезная технология, для указания поисковым системам где находится контент. Они конечно могут найти его и по ссылкам, но это дает возможность указать в первую очередь новые и изменившиеся страницы. Её поддерживают основные поисковики – Гугл, Яндекс, Яху, Лайв.

Этот протокол изначально планировался легко расширяемым (в кратце – это просто xml файл) и гугл уже добавил полезные дополнения. Это раширение протокола – Video Sitemaps. Оно полезно для разнообразных видеохостингов – указывает дополнительную информацию о видео, проигрывающихся на страницах. Гугл его использует при поиске по видео (http://video.google.com). Поэтому если у вас на сайте публикуется видео – настоятельно рекомендую обратить внимание. Также надеюсь, что Яндекс на своем поиске по видео http://video.yandex.ru тоже будет его использовать.

А теперь, по уже наметившейся традиции, хватит теории и немного практики. Конечно на .NET :)

В .NET 3.5 стало намного проще и изящнее создавать xml документы в коде с помощью LINQ to XML.
Вот небольшие выдержки из кода на FilmOnline.ru video sitemap сделан для трейлеров фильмов:

    /// <summary>

    /// For protocols details see https://www.google.com/webmasters/tools/docs/en/protocol.html

    /// </summary>

    public static readonly XNamespace sitemap =

      "http://www.sitemaps.org/schemas/sitemap/0.9";

 

     ...

 

    private static XDocument CreateSitemap(IEnumerable<MyPair<string, DateTimeOffset?>> locations,


      Func<string, XElement, XElement> processor)


    {


      return new XDocument(


        new XElement(sitemap + "urlset",


          locations


            .Where(loc => !string.IsNullOrEmpty(loc.First))


            .Select(loc => processor(loc.First,


              new XElement(sitemap + "url",


                new XElement(sitemap + "loc", loc.First),


                loc.Second.CoalesceEx(date =>


                  new XElement(sitemap + "lastmod",


                    date.Value.ToString("yyyy-MM-ddTHH:mm:ss%K")))


                )))));


    }


 


    ...


 


    /// <summary>


    /// For protocols details see http://www.google.com/support/webmasters/bin/answer.py?answer=80472&topic=10079


    /// </summary>


    public static readonly XNamespace video =


      "http://www.google.com/schemas/sitemap-video/1.0";


 


    ...


 


          element.Add(new XElement(video + "video", 


            new XElement(video + "content_loc", trailer.Data.AbsoluteUri),


            new XElement(video + "title", trailer.Title),


            trailer.Thumbnail.CoalesceEx(thumb =>


              new XElement(video + "thumbnail_loc", thumb.AbsoluteUri))));



 



Пример результата можно посмотреть, например, здесь: http://filmonline.ru/Sitemap/External/Trailers/www.afisha.ru/movie/trailer/190429/.

пятница, 3 октября 2008 г.

Cooliris (Piclens) на своем сайте

Cooliris это прикольный плагин для всех основных браузеров для просмотра картинок и видео с веб-страниц. На словах звучит довольно пространно, так что лучше просто скачать и посмотреть.

Работает он на большинстве фото и видео хостинговых сайтов (YouTube, Flickr, Picasa Web, Яндекс.Фотки). Если в кратце – данные берет он из каналов RSS с раширением Media RSS (введеным Yahoo). Т.е. для того, что бы он заработал на собственном сайте надо просто cоздать такой RSS канал и сделать чтобы он автоматически находился (RSS Autodiscovery). Если более детально – то читаем расширенное руководство для разработчиков.

С помощью .NET 3.5 это сделать довольно просто – берем SyndicationFeed, заполняем его данными. К каждому элементу не забываем добавить необходимые расширения из Media RSS через свойство ElementExtensions.

Пример его работы можно наблюдать на FilmOnline.ru на страницах с описанием фильмов – там показываются трейлеры, постеры и кадры. Например, здесь (постеры) или здесь (трейлеры).

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