понедельник, 11 апреля 2011 г.

Вопросы для собеседования по SharePoint Foundation 2010

Чтобы эффективно писать под SharePoint, нужно знать много всякой всячины. Причем, стандартный тест по ASP.Net тут как раз не поможет! Потому что нужно обязательно знать возможности и особенности платформы, чтобы не терять время на велосипедописание.

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

Несколько важных замечаний:
1. Ни на одном серьезном собеседовании по SharePoint я лично пока не присутствовал, так что никакого плагиата в этих вопросах нет, если совпадения есть, они случайны. Честно!
2. Вопросы не охватывают всю платформу, сосредотачиваясь на более общих знаниях. Написание вопросов - дело довольно долгое, поэтому вопросов по таким важным вещам, как BCS, WorkFlow, и некоторым другим очень важным темам (а также по SharePoint Server) - пока нет :) Возможно, напишу попозже.
3. Вопросы вида "Вам поручили сделать то-то, как вы это реализуете?" предполагают, что собеседуемый должен назвать общий алгоритм решения проблемы, и средства/технологии платформы, которые он при этом будет использовать. Код не надо писать))

Не смею больше томить! Вопросы под катом.

среда, 6 апреля 2011 г.

SharePoint 2010 Fluent Ribbon API

На мой взгляд, Ribbon - это абсолютно лучшее нововведение 2010й версии SharePoint. Но для разработчиков Ribbon не настолько удобен, как он удобен и привычен для пользователей. И это нужно исправлять!

Поэтому, представляю уважаемым читателям еще один мой OpenSource-проект, SharePoint 2010 Fluent Ribbon API!


Этот проект нацелен на упрощение работы с Ribbon'ом в SharePoint'е. Как известно, программное создание Ribbon'а требует выполнения большого числа действий, которые мне удалось спрятать в сборке FluentRibbon.

FluentRibbon 1.0 включают следующие основные классы:
  1. ContextualWebPart - базовый класс для веб-частей с контекстными вкладками риббона
  2. RibbonLayoutsPage - базовый класс для Application Page с риббоном

При этом, возможно динамическое создание риббона (например, в зависимости от привилегий текущего пользователя.

Chess WebPart

Вместе с FluentRibbon поставляется проект-пример ChessWebPart - веб-часть с javascript-шахматами, управление игрой и настройки в которой вынесены на риббон:

суббота, 2 апреля 2011 г.

EnhancedWebPart

Выпустил свой первый OpenSource-проект на CodePlex. Не то, чтобы очень уж полезный, скорее "проба пера". И все-таки, кому-то может пригодиться.

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

Основная неприятность, с которой сталкиваешься при разработке веб-частей SharePoint - это конечно же, несовершенство системы редактирования их свойств:
  1. Поддерживаются только самые простые типы свойств.
  2. Нет встроенного механизма для реализации локализации названий и описаний свойств.
  3. Перечисления (enum) имеют существенные ограничения при отображении, в том числе отсутствие возможности для их локализации и невозможность отображения значений с пробелами, спецсимволами, а также чисел.
Поэтому, рано или поздно, все начинают пользоваться встроенным механизмом расширений редактора свойств - через EditorPart'ы. Но поскольку EditorPart'ы - вещь довольно низкоуровневая, и архитектурно неидеальная; то с этого момента начинается фактически велосипедописание.

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

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

Основные преимущества EnhancedWebPart:
  1. Чтобы использовать функционал EnhancedWebPart, необходимо унаследовать собственную веб-часть от этого класса, и отметить специальными атрибутами нужные свойства. EnhancedWebPart самостоятельно создаст один или несколько (для каждого уникального значения атрибута Category) EditorPart'ов, и наполнит их соответствующими контролами.
  2. Простой и гибкий механизм для добавления новых отображаемых классов. Для этого используется MEF.
  3. В настоящее время в "библиотеке" отображаемых классов, поставляемой вместе с EnhancedWebPart, присутствуют следующие классы: bool, int, string, enum и SPUser.
  4. Поддерживается локализация и отображение любых значений в списках (через enum).
Недостаток пока вижу один: способ отображения зависит только от типа свойства, т.е. два способа отображения для string создать нельзя. Решения этой проблемы есть, возможно, в будущих версиях реализую.

На CodePlex'е я довольно подробно (правда, по-английски), с примерами, задокументировал этот проект. Так что вот, представляю:

четверг, 31 марта 2011 г.

Медленный DataTable?!

Вроде бы, как же так, DataTable, специальный класс для работы с данными. Причем, с большими объемами данных. И все-таки, проблемы с производительностью могут возникнуть даже с ним!

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

Простой тест!

Создадим широкую таблицу, 100 колонок (типа String). Добавим в неё пустяковый, в общем-то, объем данных - 3000 строк. Пробежимся по всей таблице, и проставим всем ячейкам значение "test".

foreach (DataRow row in table.Rows)
  foreach (DataColumn column in table.Columns)
    row[column] = "test";

Замерим время выполнения...
00:00:01.7110978

Ого! Нехило, для такой, вроде бы простой операции!? 2 секунды на каких-то жалких 3000 строк!?

И заметьте, никакого извращенного кода. Вполне нормальный код, который мог бы написать любой из нас. Единственная особенность - широкая таблица, много колонок.

Вот и у нас, в одном из совершенно реальных проектов, обнаружилась широкая таблица на 70 колонок. И с ней возникла похожая проблема. Пришлось немало повозиться, чтобы локализовать эту проблему и понять, в чем дело. И еще полчаса на то, чтобы выяснить, как это исправить.

Решение оказалось довольно неожиданным. Оказывается, в случае широких таблиц, быстрее удалить целую строку и добавить новую (фактически пересоздав таблицу), чем заполнять строки стандартным способом.

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

Measure("Standard method", 1, () =>
{
    foreach (DataRow row in table.Rows)
        foreach (DataColumn column in table.Columns)
        {
            int value = Convert.ToInt32(row[column]);
            if (value > 100)
                row[column] = value * 2;
            else
                row[column] = value * 3;
        }
});

Ничего сложного. Если значение больше 100, умножаем его на 2, иначе на 3.
А теперь то же самое, но с пересозданием строк:

Measure("Row recreate method", 1, () =>
{
    for (int i = 0; i < tempTable.Rows.Count; i++)
    {
        List<object> values = new List<object>();
        foreach (DataColumn column in tempTable.Columns)
        {
            int value = Convert.ToInt32(tempTable.Rows[0][column]);
            if (value > 100)
                values.Add(value * 2);
            else
                values.Add(value * 3);
        }

        tempTable.Rows.RemoveAt(0);
        tempTable.Rows.Add(values.ToArray());
    }
});

Да уж! Выглядит похуже, а уж читается совсем плохо.
Но давайте потестируем производительность этих двух вариантов кода на таблицах различной конфигурации...

100 колонок, 3000 строк:
Standard method => 00:00:02.3351335
Row recreate method => 00:00:00.1030059


10 колонок, 30000 строк:
Standard method => 00:00:00.2970170
Row recreate method => 00:00:00.1980114


1 колонка, 300000 строк:
Standard method => 00:00:00.1740099
Row recreate method => 00:00:01.2810733


Результаты налицо: для большинства таблиц вариант с пересозданием строк будет работать очень быстро.

P. S.  Исходники метода Measure можно взять из статьи про производительность Lua Interface.

вторник, 29 марта 2011 г.

Интеграция SharePoint с внешними приложениями через SSIS

Стандартными средствами интеграции SharePoint-списков и сторонних приложений являются BCS (Business Connectivity Services, в 2007й версии - Business Data Catalog). Я думаю, большинство SharePoint-разработчиков не раз и не два имели дело с BCS. И вот по проcшествии некоторого времени, всегда оказывается, что для некоторых задач BCS не подходят! :(

Фактически, BCS без проблем позволяет решать только примитивные задачи, ну к примеру заCRUDить табличку SQL в список SharePoint. А чуть более сложные задачи требуют либо больших телодвижений, либо ну ОЧЕНЬ больших телодвижений... :(

Не вдаваясь в подробности, можно сказать, что BCS по-прежнему не идеален.

Антон Вишняков, за это ему отдельное спасибо, открыл мне глаза на еще один способ (помимо, конечно, "писать все руками") для интеграции данных SharePoint с внешними приложениями, который иногда может оказаться весьма полезным.

Способ заключается в использовании SQL Server Integration Services (SSIS) совместно с SharePoint List Source and Destination из OpenSource-проекта Microsoft SQL Server Community Samples: Integration Services.

Хочется немного рассказать об этом способе "на уровне теории", надеюсь эта информация будет полезна.

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


Пример схемы преобразования данных в SSIS
В качестве преобразований данных могут выступать, к примеру:
  • Разделение и соединение данных - как вертикально (по столбцам), так и горизонтально (аналогично union all).
  • Сортировка
  • Группировка
  • Подведение итогов
  • Создание сводных таблиц (Pivot table)
  • Собственные виды преобразований

В качестве источника данных и приемника данных могут выступать, в простейших случаях, таблицы и базы данных MS SQL, но также это могут быть Excel-таблицы, соединения OLE DB и  ADO.Net, и т.д. Помимо встроенных источников и приемников, можно создавать собственные.

Источник и приемник для списков SharePoint в состав SSIS не входят, они были написаны отдельно и представляют собой решение Visual Studio, частично на C#, частично на VB.Net. Причем, это решение подойдет даже для 2007го SharePoint'а, поскольку не использует Client Object Model, а действуют через веб-сервис lists.asmx.

Следует отметить, что сделано все довольно дотошно. В частности, ребята постарались озаботиться такой немаловажной штукой, как производительность:
  • Источник данных списка SharePoint запрашивает не весь список, а только затребованный его объем. Выборку колонок можно ограничить через дизайнер, а также можно задавать произвольные CAML-запросы.
  • Есть настройка, которая позволяет задать количество строк "за этап". Для некоторых "широких" (с большим числом столбцов) списков такое вот блочное чтение позволяет увеличить итоговую производительность.
  • Приемник данных обновляет только нужные колонки и только нужные строки (через Batch API).
И все это - не забываем, Open Source. Есть возможность допилить.

Если решите попробовать, пример по созданию SSIS-проекта для интеграции с SharePoint описан на MSDN.

P.S. Сам пока не пробовал, так что это все пока только в теории, но думаю, этот способ вполне имеет шансы на успех. Надеюсь, будет возможность где-нибудь попробовать на рабочем проекте, если это удастся - я отпишусь! :)

понедельник, 21 марта 2011 г.

Локализация SharePoint: кодогенерация ресурсных классов

SharePoint обладает одной довольно неприятной особенностью в плане локализации: он плохо дружит со стандартными средствами ASP.Net в этой области. Фактически, в SharePoint-проектах желательно везде использовать локализацию через SPUtility.GetLocalizedString, причем в качестве параметра language нужно обязательно передавать свойство LCID объекта Thread.CurrentThread.CurrentUICulture (чтобы работала on-fly локализация).

Соответственно, даже обернув вызов GetLocalizedString в какие-то минимальные врапперы, все равно от нехороших конструкций вида LocalizationHelper.Localize("ResourceName", "ResourceFile") уйти не удастся. Основная проблема - это, конечно, наличие в таком коде большого количества "magic strings", со всеми вытекающими.

Частично от этого спасает введение класса, к примеру, ResourceNames, который хранит константы для всех названий ресурсов. Однако, в этом случае приходится содержать целый лишний класс, поддерживать его, да и от magic strings мы таким образом не избавляемся, а просто держим их отдельно.

Достаточно долгое время наш основной рабочий проект работал именно по приведенной выше схеме с magic strings. Причем, когда я (еще летом 2010) исследовал возможности и средства локализации проекта, естественно искал в интернетах - но никаких других толковых решений не нашел.

И вот один мой коллега реализовал другое решение - на основе кодогенерации. С кодогенерацией я работал очень мало, по сути только полгода назад узнал о том, что это такое. Но идея-то очень логичная, и совершенно изумительная в плане результата: полностью избавляемся от magic strings, и получаем все преимущества интеллисенса.

В результате применения кодогенерации, становится возможным использовать (причем, Intellisense при этом отлично работает) конструкции вида: Resources.ResourceFileName.ResourceName. Для нашего основного проекта я сделал еще интереснее. Поскольку ресурсов там очень много, и все именуются по стандартному принципу, мне удалось сделать бОльшую вложенность классов, и за счет этого упростить выбор ресурса. Т.е. у нас получилось что-то вроде такого: Resources.ResourceFileName.ProjectName.ModuleName.ResourceName.

Более того, мне также удалось добиться отображения комментариев (колонка Comment в файле resx) через Intellisense! Выглядит все это весьма эффектно:


В общем, в этом посте я расскажу про особенности реализации, те же кому важен конечный результат, могут стянуть сразу tt-файлик (в конце поста).

среда, 16 марта 2011 г.

Sandcastle, MAML, и документирование ПО

Как известно, Visual Studio позволяет писать комментарии в коде в xml-формате, если они предваряются тройным слешем ("///"). Большинство при этом используют лишь пару простых тегов, как например <summary> и <param>.

На самом деле, тегов там можно использовать довольно много. Наиболее полезные, на которые я б сразу посоветовал обратить внимание - это <see cref="">, <example>, <para>, и <remarks>. Также, иногда очень полезен будет тег <list>. Более подробно обо всем этом ниже.

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


Мне пришлось разобраться со всем этим, когда разрабатывал описание API для одного из наших клиентов. И настолько мне понравился результат, что по собственной инициативе задокументировал некоторые неймспейсы основного нашего проекта, и сгенерировал по ним chm-файлик (он как раз представлен на скриншоте выше). Коллегам эта затея пришлась по душе, так что надеюсь в дальнейшем общими усилиями нам удастся эту линию продолжить, и создать документацию для большей части общих сборок.

В общем, в этом сообщении я расскажу немного про язык MAML, про инструменты для генерации документации (особенно про SandCastle), ну и вообще про то, как это все настраивать и как с этим всем работать.