Чтобы эффективно писать под SharePoint, нужно знать много всякой всячины. Причем, стандартный тест по ASP.Net тут как раз не поможет! Потому что нужно обязательно знать возможности и особенности платформы, чтобы не терять время на велосипедописание.
Вот я и задумался, какие бы вопросы я сам задал, если бы мне пришлось собеседовать SharePoint-программиста, чтобы узнать широту и глубину его знаний.
Несколько важных замечаний:
1. Ни на одном серьезном собеседовании по SharePoint я лично пока не присутствовал, так что никакого плагиата в этих вопросах нет, если совпадения есть, они случайны. Честно!
2. Вопросы не охватывают всю платформу, сосредотачиваясь на более общих знаниях. Написание вопросов - дело довольно долгое, поэтому вопросов по таким важным вещам, как BCS, WorkFlow, и некоторым другим очень важным темам (а также по SharePoint Server) - пока нет :) Возможно, напишу попозже.
3. Вопросы вида "Вам поручили сделать то-то, как вы это реализуете?" предполагают, что собеседуемый должен назвать общий алгоритм решения проблемы, и средства/технологии платформы, которые он при этом будет использовать. Код не надо писать))
Не смею больше томить! Вопросы под катом.
понедельник, 11 апреля 2011 г.
среда, 6 апреля 2011 г.
SharePoint 2010 Fluent Ribbon API
На мой взгляд, Ribbon - это абсолютно лучшее нововведение 2010й версии SharePoint. Но для разработчиков Ribbon не настолько удобен, как он удобен и привычен для пользователей. И это нужно исправлять!
Поэтому, представляю уважаемым читателям еще один мой OpenSource-проект, SharePoint 2010 Fluent Ribbon API!
Этот проект нацелен на упрощение работы с Ribbon'ом в SharePoint'е. Как известно, программное создание Ribbon'а требует выполнения большого числа действий, которые мне удалось спрятать в сборке FluentRibbon.
FluentRibbon 1.0 включают следующие основные классы:
При этом, возможно динамическое создание риббона (например, в зависимости от привилегий текущего пользователя.
Chess WebPart
Вместе с FluentRibbon поставляется проект-пример ChessWebPart - веб-часть с javascript-шахматами, управление игрой и настройки в которой вынесены на риббон:
Поэтому, представляю уважаемым читателям еще один мой OpenSource-проект, SharePoint 2010 Fluent Ribbon API!
Этот проект нацелен на упрощение работы с Ribbon'ом в SharePoint'е. Как известно, программное создание Ribbon'а требует выполнения большого числа действий, которые мне удалось спрятать в сборке FluentRibbon.
FluentRibbon 1.0 включают следующие основные классы:
- ContextualWebPart - базовый класс для веб-частей с контекстными вкладками риббона
- RibbonLayoutsPage - базовый класс для Application Page с риббоном
При этом, возможно динамическое создание риббона (например, в зависимости от привилегий текущего пользователя.
Chess WebPart
Вместе с FluentRibbon поставляется проект-пример ChessWebPart - веб-часть с javascript-шахматами, управление игрой и настройки в которой вынесены на риббон:
суббота, 2 апреля 2011 г.
EnhancedWebPart
Выпустил свой первый OpenSource-проект на CodePlex. Не то, чтобы очень уж полезный, скорее "проба пера". И все-таки, кому-то может пригодиться.
По названию вы наверняка поняли, что речь идет об усовершенствовании базового функционала веб-частей.
Основная неприятность, с которой сталкиваешься при разработке веб-частей SharePoint - это конечно же, несовершенство системы редактирования их свойств:
Поэтому логично было создать некий универсальный механизм, который обеспечивал бы реализацию некоторых дополнительных функций для редактора свойств веб-частей, и содержал бы легко расширяемую библиотеку самых частоиспользуемых контролов.
Собственно, моя попытка реализовать эту идею и получила название EnhancedWebPart.
Основные преимущества EnhancedWebPart:
На CodePlex'е я довольно подробно (правда, по-английски), с примерами, задокументировал этот проект. Так что вот, представляю:
По названию вы наверняка поняли, что речь идет об усовершенствовании базового функционала веб-частей.
Основная неприятность, с которой сталкиваешься при разработке веб-частей SharePoint - это конечно же, несовершенство системы редактирования их свойств:
- Поддерживаются только самые простые типы свойств.
- Нет встроенного механизма для реализации локализации названий и описаний свойств.
- Перечисления (enum) имеют существенные ограничения при отображении, в том числе отсутствие возможности для их локализации и невозможность отображения значений с пробелами, спецсимволами, а также чисел.
Поэтому логично было создать некий универсальный механизм, который обеспечивал бы реализацию некоторых дополнительных функций для редактора свойств веб-частей, и содержал бы легко расширяемую библиотеку самых частоиспользуемых контролов.
Собственно, моя попытка реализовать эту идею и получила название EnhancedWebPart.
Основные преимущества EnhancedWebPart:
- Чтобы использовать функционал EnhancedWebPart, необходимо унаследовать собственную веб-часть от этого класса, и отметить специальными атрибутами нужные свойства. EnhancedWebPart самостоятельно создаст один или несколько (для каждого уникального значения атрибута Category) EditorPart'ов, и наполнит их соответствующими контролами.
- Простой и гибкий механизм для добавления новых отображаемых классов. Для этого используется MEF.
- В настоящее время в "библиотеке" отображаемых классов, поставляемой вместе с EnhancedWebPart, присутствуют следующие классы: bool, int, string, enum и SPUser.
- Поддерживается локализация и отображение любых значений в списках (через enum).
На CodePlex'е я довольно подробно (правда, по-английски), с примерами, задокументировал этот проект. Так что вот, представляю:
четверг, 31 марта 2011 г.
Медленный DataTable?!
Вроде бы, как же так, DataTable, специальный класс для работы с данными. Причем, с большими объемами данных. И все-таки, проблемы с производительностью могут возникнуть даже с ним!
Вы скажете: если захотеть, всегда можно создать себе проблемы. Но здесь случай не такой.
Простой тест!
Создадим широкую таблицу, 100 колонок (типа String). Добавим в неё пустяковый, в общем-то, объем данных - 3000 строк. Пробежимся по всей таблице, и проставим всем ячейкам значение "test".
Замерим время выполнения...
00:00:01.7110978
Ого! Нехило, для такой, вроде бы простой операции!? 2 секунды на каких-то жалких 3000 строк!?
И заметьте, никакого извращенного кода. Вполне нормальный код, который мог бы написать любой из нас. Единственная особенность - широкая таблица, много колонок.
Вот и у нас, в одном из совершенно реальных проектов, обнаружилась широкая таблица на 70 колонок. И с ней возникла похожая проблема. Пришлось немало повозиться, чтобы локализовать эту проблему и понять, в чем дело. И еще полчаса на то, чтобы выяснить, как это исправить.
Решение оказалось довольно неожиданным. Оказывается, в случае широких таблиц, быстрее удалить целую строку и добавить новую (фактически пересоздав таблицу), чем заполнять строки стандартным способом.
Давайте проверим.
Для того, чтобы пример был более реалистичным, я забил таблицу случайными целочисленными значениями, и в дальнейшем использовал для тестирования следующий код:
Ничего сложного. Если значение больше 100, умножаем его на 2, иначе на 3.
А теперь то же самое, но с пересозданием строк:
Да уж! Выглядит похуже, а уж читается совсем плохо.
Но давайте потестируем производительность этих двух вариантов кода на таблицах различной конфигурации...
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.
Вы скажете: если захотеть, всегда можно создать себе проблемы. Но здесь случай не такой.
Простой тест!
Создадим широкую таблицу, 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 позволяет задать источники данных, затем совершить над ними какие-нибудь преобразования, и конечный результат поместить в некий приемник данных.
В качестве преобразований данных могут выступать, к примеру:
В качестве источника данных и приемника данных могут выступать, в простейших случаях, таблицы и базы данных MS SQL, но также это могут быть Excel-таблицы, соединения OLE DB и ADO.Net, и т.д. Помимо встроенных источников и приемников, можно создавать собственные.
Источник и приемник для списков SharePoint в состав SSIS не входят, они были написаны отдельно и представляют собой решение Visual Studio, частично на C#, частично на VB.Net. Причем, это решение подойдет даже для 2007го SharePoint'а, поскольку не использует Client Object Model, а действуют через веб-сервис lists.asmx.
Следует отметить, что сделано все довольно дотошно. В частности, ребята постарались озаботиться такой немаловажной штукой, как производительность:
Если решите попробовать, пример по созданию SSIS-проекта для интеграции с SharePoint описан на MSDN.
P.S. Сам пока не пробовал, так что это все пока только в теории, но думаю, этот способ вполне имеет шансы на успех. Надеюсь, будет возможность где-нибудь попробовать на рабочем проекте, если это удастся - я отпишусь! :)
Фактически, 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).
Если решите попробовать, пример по созданию 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-файлик (в конце поста).
Соответственно, даже обернув вызов 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), ну и вообще про то, как это все настраивать и как с этим всем работать.
На самом деле, тегов там можно использовать довольно много. Наиболее полезные, на которые я б сразу посоветовал обратить внимание - это <see cref="">, <example>, <para>, и <remarks>. Также, иногда очень полезен будет тег <list>. Более подробно обо всем этом ниже.
Оказывается, эти комментарии видны не только в интеллисенсе, но также на их основе можно сгенерировать CHM-файл или справочный веб-сайт, с помощью специально предназначенных для этого, общедоступных инструментов. Внешний вид получается красивый, и главное очень всем известный - всем, кто хоть раз бывал на MSDN :) Пользоваться такой справкой довольно удобно.
Мне пришлось разобраться со всем этим, когда разрабатывал описание API для одного из наших клиентов. И настолько мне понравился результат, что по собственной инициативе задокументировал некоторые неймспейсы основного нашего проекта, и сгенерировал по ним chm-файлик (он как раз представлен на скриншоте выше). Коллегам эта затея пришлась по душе, так что надеюсь в дальнейшем общими усилиями нам удастся эту линию продолжить, и создать документацию для большей части общих сборок.
В общем, в этом сообщении я расскажу немного про язык MAML, про инструменты для генерации документации (особенно про SandCastle), ну и вообще про то, как это все настраивать и как с этим всем работать.
Подписаться на:
Сообщения (Atom)





