вторник, 8 ноября 2011 г.

Введение в рабочие процессы SharePoint 2010

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


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

Впрочем, как это всегда бывает в SharePoint, существует множество тонкостей, нюансов и особенностей, которые нужно знать и иметь в виду, прежде чем говорить клиенту/начальнику о том, что вот мол на решение этой задачи уйдет полдня :)

Итак, давайте рассмотрим вкратце, что представляют собой рабочие процессы в SharePoint. Эта статья является стартовой в новой серии статей про рабочие процессы.

среда, 26 октября 2011 г.

SharePoint и XSLT: преобразуем колонку в строку

В посте про объединение двух колонок в одну я показывал на "боевом" примере про комментарий руководителя, как можно сэкономить место на странице и сделать представление более привлекательным, объединяя несколько колонок в одну. С помощью XSLT, мы производили вот такое преобразование:


Но как видите, всё равно в строке с комментарием остается много пустого места:



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


Вперед?

вторник, 25 октября 2011 г.

SharePoint и XSLT: Развертывание через Visual Studio

К настоящему времени, в рамках серии статей про SharePoint и XSLT, уже были освещены многие интересные моменты, включая довольно актуальные примеры (разбор еще множества примеров - впереди), но до сих пор мы не касались развертывания XSL-преобразований через Visual Studio. Эта информация очень важна для разработчиков SharePoint, но замечу еще раз - что посредством SharePoint Designer, XSL-преобразованиями в SharePoint могут пользоваться и администраторы, и дизайнеры, и ITPro.

Итак, сегодня мы узнаем:
  1. Как подключать XSLT через CAML
  2. Как программно подключить XSL-преобразование к списку
  3. Чем отличаются свойства XslLink классов SPView и XsltListViewWebPart
  4. Ограничения при использовании XSLT в Sandboxed solutions

вторник, 18 октября 2011 г.

Про XSLT и Visual Studio: отладка и intellisense

По результатам публикации статьи SharePoint и XSLT: Hello world!, уже появились недовольные читатели, восклицающие в комментариях, что вот мол, использовать Блокнот/SharePoint Designer это непрофессионально... :) Примерно такие же отзывы я слышал и на RUSUG, где недавно читал доклад "SharePoint и XSLT".

И на самом деле я согласен, что будучи разработчиком, и имея такой мощный и многофункциональный инструмент, как Visual Studio, хотелось бы использовать его богатые возможности для работы с XSLT в SharePoint. Точнее, хотя бы попробовать это сделать :))

Ну вот давайте посмотрим, на что способна (или не способна?) Visual Studio.

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

SharePoint и XSLT: Объединяем две колонки в одну

Довольно часто возникает такая задача: объединить несколько колонок списка в одну при отображении. Обычно это требуется в следующих случаях:
  1. Когда в списке ну очень много колонок, поэтому при отображении эти данные поневоле приходится группировать
  2. Когда несколько колонок логично было бы объединить в один блок
  3. Иногда в некоторых колонках картинки или длинные текстовые описания, поэтому поля получаются очень разными по высоте, и оттого выглядят некрасиво
  4. Когда часть данных в списке важна при отображении, но присутствует редко. Например, комментарий от руководителя:

В общем, объединение двух колонок в одну при отображении позволяет сэкономить место на экране, улучшить внешний вид, поправить usability, "вписаться" в брэндбук... Это требуется часто, поэтому важно знать, как это можно сделать.

четверг, 13 октября 2011 г.

SharePoint и XSLT: Hello world!

Продолжаю серию статей про XSLT в SharePoint.

В предыдущей статье я рассказывал, почему XSLT в SharePoint - это важно.

Сегодня - перейду по-тихоньку к практике, и вы узнаете: как написать простейшее XSL-преобразование, как подключить его к списку, как выбрать шаблон для изменения, где подсмотреть примеры XSLT-кода, как выглядит дерево шаблонов веб-части XsltListViewWebPart, и еще много разных нюансов :) Поехали?

вторник, 11 октября 2011 г.

SharePoint Designer

Давно хотел написать :)

За первый год работы с SharePoint, не использовал SharePoint Designer (далее SPD) ни разу. Питая с еще времен Perl'а стойкое отвращение к FrontPage, всегда считал любые визуальные редакторы HTML чем-то вроде костылей для людей, которые пока еще не выучили HTML...


В общем, не помню с чего это началось, но пришлось мне всё-таки установить, в конце концов, это "лишнее" приложение на рабочий компьютер... Ну, а раз установил - то полазал, пощупал, посмотрел - что к чему. Оказалось: штука очень полезная (именно для разработчика), и от FrontPage там мало что осталось. А вот различных интересных вещей можно подсмотреть - множество. И вот уже полгода - без SPD, как без третьей руки :)

Нет, код конечно можно писать и без него, но постепенно SPD встал в один ряд с рефлектором, MSDN и с "investigate 14 hive" в плане исследований. А исследования у меня занимают нередко большую часть всего времени разработки какого-либо решения - потому что оказывается, что очень многое в SharePoint можно сделать имеющимися средствами, быстро и эффективно, сохраняя глубокую интеграцию с другими частями системы. И даже несмотря на то, что на исследования тратится очень много времени - результат, я считаю, того стоит.

Ну да, и демонстрировать какие-то вещи на докладах - тоже очень удобно именно через SharePoint Designer. И на SharePoint Conference Russia, и на последней RUSUG - использовал именно его для демонстраций.

пятница, 7 октября 2011 г.

SharePoint и XSLT: Почему XSLT - это важно?

Начинаю серию статей по XSLT в SharePoint.

XSLT в SharePoint - чрезвычайно мощный инструмент, глубоко интегрированный в систему. Даже в предыдущей, 2007й версии, роль XSLT была довольно велика - преобразования использовались при отображении результатов поиска (CoreResultsWebPart), а также для кастомизаций отображения представлений и форм списков (DataViewWebPart и DataFormWebPart).

В 2010м SharePoint'е XSL-преобразования используются еще более интенсивно. Отображение представлений всех стандартных списков, кроме календарей, осуществляется теперь именно через XSLT (XsltListViewWebPart). Формы внешних списков также отображаются через XSLT (XlstListFormWebPart), так что тенденция видна невооруженным взглядом: поскольку данные в SharePoint представляются в виде списков, на которые завязано всё, то можно смело сказать:
Практически всё отображение данных в SharePoint'e осуществляется с помощью XSLT

суббота, 1 октября 2011 г.

9 статей про SharePoint


Прошло уже больше года с момента основания блога, и давно пора подвести некоторые итоги. За год, я опубликовал чуть больше 50 постов, большинство из которых - про SharePoint, и среди последних лично я выделяю девять, тянущих на звание "статья".

Я думаю, многие из моих читателей пропустили хотя бы 1-2 статьи из этого списка. Поэтому, милости прошу, чтиво на выходные:
  1. Использование HTML5 в формах списков SharePoint
  2. Локализация SharePoint: кодогенерация ресурсных файлов
  3. SP.UI.ModalDialog и стандартные диалоги SharePoint
  4. SMS и SharePoint
  5. SharePoint: что такое хорошо, и что такое плохо
  6. Чем плох CAML в SharePoint
  7. Работа с XML из SharePoint javascript
  8. Вопросы для собеседования по SharePoint Foundation 2010
  9. Как удалять и заменять OOTB кнопки на Ribbon'е
Также,  если кто-то вдруг не в курсе, в конце августа сего года я создал еще один блог, английский, и уже успел настрочить туда 6 постов, из которых два даже были опубликованы на сайте NothingButSharePoint.com:
  1. 3 ways to localize SharePoint XSLT files
  2. Selected items query with Javascript

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

четверг, 1 сентября 2011 г.

Чем плох CAML в SharePoint?

В последнее время, я всё чаще и чаще пишу обертки для CAML. Началось всё, кстати, с моего любимого SharePoint 2010 Fluent Ribbon API, благодаря которому я очень глубоко изучил Ribbon. Кстати, ведь забыл в блог написать, всего пару дней назад, зарелизил версию 1.3! :)

Создание кнопки на ленте с помощью Fluent Ribbon 


среда, 31 августа 2011 г.

Видео доклада SharePoint Ribbon и Office 365

Видеозапись моего доклада с SharePoint Conference Russia наконец-то опубликована (как, впрочем, и видеозаписи докладов всех других участников)!

Те, кто еще не смотрел, могут посмотреть.

вторник, 30 августа 2011 г.

Метаданные для SPField на стороне клиента

В недавнем посте я предлагал способ для запоминания метаданных SPField. И вот сегодня еще раз убедился, что пошел правильным путем.

Возникла задача, получить метаданные поля на стороне клиента, используя SharePoint Client Object Model. Конкретно, нужно было получить все поля, помеченные как содержащие E-mail - у пользователей для простановки этих отметок есть соответствующий интерфейс.

В итоге, пришел вот к такой простейшей функции:

function getFieldAttribute(field, metadataKey) {
    /// <summary>
    /// This function gets metadata key
    /// </summary>
    var xml = field.get_schemaXml();
    var domElement = CUI.NativeUtility.createXMLDocFromString(xml);
 
    var attribute;
 
    if (domElement.firstChild.getAttributeNS != null && domElement.firstChild.getAttributeNS != undefined)
        attribute = domElement.firstChild.getAttributeNS('my', metadataKey);
    else
        attribute = domElement.firstChild.getAttribute('p1:' + metadataKey);
 
    return attribute;
}
 

Как видите, всё довольно просто. Когда расширять и интегрировать созданное решение просто - это хороший признак того, что делаешь правильно.

Использовать - примерно так:

var ctx = new SP.ClientContext.get_current();
var list = ctx.get_web().get_lists().getById(SP.ListOperation.Selection.getSelectedList());
 
var fields = list.get_fields();
ctx.load(fields);
 
// ... load items or whatever you need here
 
ctx.executeQueryAsync(onSuccess, onFailure);
 
function onSuccess() {
    var emailFields = new Array();
 
    var fieldEnumerator = fields.getEnumerator();
    while (fieldEnumerator.moveNext()) {
 
        var field = fieldEnumerator.get_current();
        var attribute = getFieldAttribute(field,'MyFieldType');
        if (attribute == 'Email')
            emailFields.push(field.get_internalName());
    }
}
 

среда, 10 августа 2011 г.

SharePoint и XSLT: содержание книги

Думаю о возможности написания книги про XsltListViewWebPart. Материала уже набирается немало, ниже приведено примерное содержание того, что я уже могу написать.

пятница, 29 июля 2011 г.

SMS и SharePoint

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

Я имею в виду встроенную в SharePoint возможность отсылать SMS и даже MMS по протоколу Office Mobile Service Protocol. Протокол вполне продуманный: предусмотрена пакетная отправка сообщений и интерфейс для получения статусов доставки... В этом посте я расскажу, как можно этот самый OMS использовать в своих решениях, и какие бонусы можно получить.

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

Работа с XML в SharePoint из Javascript

SharePoint очень тесно связан с XML. Все эти схемы полей и списков, определения элементов Ribbon, SharePoint Batch API, запросы SPQuery/CamlQuery - и многое другое. И если на стороне сервера всё более-менее понятно, там есть XmlReader/XmlWriter, XmlSerializer, XDocument и т.д., то что делать на стороне клиента?

Как обрабатывать XML, формировать или изменять его, и какие для этого есть функции в SharePoint EcmaScript  - всю эту информацию я постарался собрать в сегодняшней статье.

вторник, 5 июля 2011 г.

Использование HTML5 в формах списков SharePoint

Недавно я писал про RenderingTemplate и использование перегруженного ListFieldIterator для того, чтобы изменять отображение форм списков SharePoint. В качестве примера использования этого способа, я привел скриншот проекта, где поля списка распределены по вкладкам. Также, в том посте был выложен для скачивания "базовый" проект-пример на эту тему.

Сегодня я хочу еще раз вернуться к RenderingTemplate и ListFieldIterator, рассмотрев их более тщательно и иллюстрированно, на другом примере - внедряя элементы управления HTML5 в формы списков SharePoint.

понедельник, 4 июля 2011 г.

SP.UI.ModalDialog и стандартные диалоги SharePoint

На своем докладе на SharePoint Conference Russia 2011 я, в числе прочего, показывал интересный пример глубокой интеграции с SharePoint - использование диалогов SharePoint в собственных целях, получение и обработка их returnValue.

Такой подход позволяет обеспечить глубокую интеграцию с SharePoint и иногда - значительно упростить разработку того или иного функционала. Особенно это актуально в среде Office365, где нет возможности создания собственных Application Pages. В этом случае крайне важно по максимуму использовать тот функционал, который есть в SharePoint.

В этой заметке я хочу еще раз вернуться к теме возвращаемых значений диалогов, описать несколько примеров, и привести код для самостоятельного тестирования диалогов, которых я не видел :)

пятница, 24 июня 2011 г.

Метаданные для SPField

У объекта SPField есть два полезных метода: GetCustomProperty и SetCustomProperty. Но к сожалению, использовать их в качестве обычного PropertyBag очень сложно:
  1. Свойства эти можно использовать только для Custom Field Type'ов, прописывая их названия и типы в схеме типа поля
  2. Вдобавок, нужно еще изменять схему экземпляра поля (SPField.Schema), добавляя туда атрибут Customization. Автоматически это почему-то не делается :(
Естественно, динамически добавлять свойства к уже существующим полям с помощью этих методов никак не получится. Однако, решения есть, и они известны. Дело в том, что схема поля (SPField.Schema) позволяет использовать произвольные атрибуты для элемента Field.

Обычно, для того, чтобы их задействовать, используют private методы класса SPField: SetFieldAttributeValue и GetFieldAttributeValue. Но в этом случае, естественно, используется Reflection, который запрещен в Office365. И вообще, Reflection - это хак, а любые хаки желательно обходить стороной.

Поэтому, я написал собственное решение, которое парзит XML и меняет свойство FieldSchema самостоятельно. Решение получилось довольно простым, и я всё чаще и чаще использую этот самодельный PropertyBag для хранения разных дополнительных атрибутов полей.

Код (не забудьте подключить пространство имен System.Xml.Linq):

  private const string nameSpace = "my";

  /// <summary>
  /// Прикрепляем метаданные к колонке списка. Эти метаданные будут храниться
  /// в схеме поля.
  /// </summary>
  /// <param name="field">Колонка (SPField), к которой следует добавить метаданные</param>
  /// <param name="key">Название поля метаданных</param>
  /// <param name="value">Значение поля метаданных</param>
  public void SetFieldMetaData(SPField field, string key, string value)
  {
   var fieldSchema = XDocument.Parse(field.SchemaXml);
   var tabAttribute = fieldSchema.Element("Field").Attribute(XNamespace.Get(nameSpace) + key);
   if (tabAttribute == null)
    fieldSchema.Element("Field").Add(new XAttribute(XNamespace.Get(nameSpace) + key, value));
   else
    tabAttribute.Value = value;

   field.SchemaXml = fieldSchema.ToString();
   field.Update();
  }

  /// <summary>
  /// Получаем метаданные из колонки.
  /// </summary>
  /// <param name="field">Колонка, из которой считываются метаданные</param>
  /// <param name="key">Название поля метаданных</param>
  /// <returns>Возвращает значение поля метаданных для колонки, или null, если поле метаданных с указанным именем не было ранее присоединено к этой колонке.</returns>
  public string GetFieldMetaData(SPField field, string key)
  {
   var fieldSchema = XDocument.Parse(field.SchemaXml);
   var tabAttribute = fieldSchema.Element("Field").Attribute(XNamespace.Get(nameSpace) + key);
   if (tabAttribute == null)
    return null;
   else
    return tabAttribute.Value;
  }

вторник, 21 июня 2011 г.

SharePoint Conference Russia 2011: краткий отчет

Вчера выступал на SharePoint Conference Russia 2011. Это моя первая конференция, первое серьезное выступление, и одновременно, как это ни смешно, вообще первая девелоперская конференция, посещенная мной лично.



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

среда, 15 июня 2011 г.

Как правильно изменять внешний вид List Form в SharePoint

Всё в SharePoint'е можно делать разными способами. Но в последнее время, очень хочется делать правильно...

На этот раз, мне потребовалось внести изменения на формы стандартного SharePoint'овского списка. Таких форм существует три вида, и вы все их прекрасно знаете:
  1. DisplayForm - форма отображения элемента списка
  2. EditForm - форма изменения элемента списка
  3. NewForm - форма создания нового элемента списка
Из-за большого количества полей в одном из наших списков, мне захотелось разнести эти поля по вкладкам. Причем, захотелось также следующее:
  1. Чтобы сохранилась возможность добавлять новые поля в список
  2. Чтобы можно было перемещать поля из одной вкладки в другую, и менять порядок их следования
  3. Чтобы поля рендерились стандартными средствами 
Для того, чтобы определить принадлежность полей к вкладкам, я раскопал способ добавления пользовательских данных в SPField, и создал симпатичный jquery-интерфейс для того, чтобы этим мог заниматься пользователь. Но дальше дело, неожиданно, застопорилось: оказалось, что народ повсеместно пользуется для подобных целей грязными js-хаками, а такой способ мне категорически не подошел!

И вот, после долгих исследований и поиска в интернетах, мне-таки удалось понять, как же это делается правильно, и воплотить в жизнь вот такую вот красивую форму:



Как видите, часть полей (ФИО и фотография) здесь рендерятся по-особому. Остальные поля рендерятся обычным образом, но разбиваются на вкладки (пользователь может менять порядок полей и принадлежность полей разным вкладкам).

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

SharePoint: что такое хорошо, и что такое плохо

Всё-таки полезно иногда, когда у тебя вырезают всякий мусор из тела :) После аппендицита, в мозгах словно перещелкнуло: довольно серьезно изменил манеру программирования под SharePoint.

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

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

Банальный пример: вам требуется как-то по-своему отобразить список. 9 из 10 просто сядут и напишут веб-часть, которая забирает данные из списка и их отображает. И потеряют гигантское количество возможностей, которые они могли бы поиметь, если бы не поленились разобраться с возможностями XsltListViewWebPart.

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

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



Это решение поведенчески полностью соответствует обычным представлениям списков: к примеру, можно выделять одну или несколько записей; работают контекстные вкладки Ribbon'а, и т.д.

И еще, как видите, здесь поля могут быть различных типов: E-mail, ссылка, телефон, мобильный телефон. Дак вот, я добился, чтобы при добавлении пользователями портала в представление новых полей, поля эти естественным образом отображаются, и для них можно даже задать тип и отобразить в правой колонке с соответствующей иконкой. Впрочем, это тема для отдельного поста.

Мораль: не ленитесь изучать SharePoint, интегрируйтесь глубже. Оно того стоит!

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

Управление подсветкой синтаксиса в Sandcastle Help File Builder

На самом деле, лично я качественной подсветке синтаксиса всегда уделяю огромное внимание. Скажем, для этого блога, я потратил часов 5 (в течение нескольких дней), чтобы найти подходящую утилиту, которая бы подсвечивала код в точности также, как это делает Visual Studio.

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

Дак вот, именно поэтому довольно куцая подсветка кода в Sandcastle Help File Builder (далее SHFB), меня не слишком обрадовала (про Sandcastle я совсем недавно писал, это утилита для генерации документации в MSDN-стиле).

И вот, когда засел писать документацию в SHFB уже в третий раз (кстати, для SharePoint Fluent Ribbon), мне все-таки удалось эту проблему победить! Теперь моя документация выглядит всем на зависть и на заглядение:

И между прочим, сделать это совсем несложно. В этой статье я расскажу, как.

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

CodePlex про SharePoint

Иногда бывает довольно полезно полазать по CodePlex'у и открыть для себя пару маленьких проектов, которые делают большие вещи :)

SharePoint 2010 automatic sign-in with mixed authentication

Данный проект ничего на ваш сайт не добавляет, как это ни смешно. Зато - убирает лишнее! А именно, вот эту надоедливую страничку:
Если у вас локальный IP-шник, будете заходить на сайт сразу под Windows-аутентификацией. Иначе - Forms.

SharePoint 2010 Developer Dashboard Visualizer

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


Drag & Drop between SharePoint Document libraries

Если положить две библиотеки документов на одну страничку, можно перетаскивать между ними документы - Drag&Drop'ом! В некоторых случаях это может оказаться очень привлекательной фишкой.

SPContentLoader for SharePoint 2010

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

SharePoint Social Networking

Добавляет кнопки популярных социальных сетей и сервисов на ваш портал.



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

Конечно, в таких случаях можно и нужно садиться, и писать самому. Но... не всегда хватает времени :(

понедельник, 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), ну и вообще про то, как это все настраивать и как с этим всем работать.

вторник, 8 февраля 2011 г.

Программируем электронную книгу Qumo Libro

Недавно приобрел себе читалку Qumo Libro (писал об этом в Buzz'е), и был совершенно уверен, что программировать её нельзя, поскольку слово "прошивка" четко обозначало (раньше, как минимум) что-то, что заливается в ППЗУ и намертво.

И только после покупки набрел на форум сайта The-eBook.org, где обнаружилось, что писать программы для этой книжки можно, да к тому же не на какой-нибудь обрезанной Java, а на самом обычном C.

Ну, дернуло и меня этим заняться, особенно всвязи с тем, что в Libro НЕТ ШАХМАТ!! :( Но на то, чтобы подступиться к программированию, потратил кучу времени. Крупицы требуемых полезных сведений были разбросаны чуть ли не по всему форуму, и стоило большого труда всё это собрать воедино.

Между прочим, Qumo Libro программно совместима с Qumo Colibri, Digma e*, Gmini, Sibrary, и другими подобными электронными книгами, поэтому данное руководство и для вышеозначенных книг тоже должно подойти.

В общем, всвязи со всем этим, решил написать небольшую статью для тех, кто вдруг тоже купит себе читалку, совместимую с Qumo Libro, и захочет для этого девайса что-нибудь написать!

четверг, 3 февраля 2011 г.

Отображаем число подписчиков на кнопке Google buzz

В поставляемых Google'ом кнопках подписки нет возможности отобразить текущее число подписчиков.

Решив этот минус исправить, сделал себе такую вот кнопку для желающих подписаться на мой Buzz (туда транслируется данный блог, а также пощу некоторые менее формальные сообщения):


Здесь скриншот, собственно кнопка - в правой колонке на блоге (это на всякий случай, для читателей по RSS и через Buzz). Если кто-то хочет себе такую же, в этом посте объясняю, как это сделать.

среда, 2 февраля 2011 г.

Пишем Wix Extension (CompilerExtension)

Насколько мне известно, WiX - это одна из наиболее развитых и продуманных XML-систем. Причем, сделано так, что этим может пользоваться даже человек. Сегодня я еще раз убедился в том, что WiX предоставляет действительно классные возможности.

Проблема

Дело в том, что у нашего продукта множество вариаций. Версии Базовый и Стандарт, различные локализации, вариации для Saas и т.д... Все это выливается в необходимость создания большого числа однотипных установщиков.

Раньше мы использовали для этого Microsoft Setup Project, однако количество багов и слабые возможности этого решения вынудили все это переделать на WiX, чем я и занялся.

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

Поиск решения

Решение вроде бы напрашивалось само собой: в библиотеке WixUIExtension имеются элементы XmlFile и XmlConfig. Однако, для генерации больших файлов они не подходят. Попробовав их использовать, я выяснил, что 10 строк обычного xml с их помощью преобразуется аж в 100(!!).

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

Поэтому, для активной генерации xml элементы XmlFile и XmlConfig явно не подходят. В процессе поиска альтернативного решения, я обратил внимание на Wix Extensions, и задумал написать собственный такой модуль расширения, который бы позволил максимально быстро и эффективно развертывать наш конкретный xml, и желательно, позволял бы использовать нашу же xsd-схему при его создании/изменении.

Например, это могло бы выглядеть примерно так:

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">

    <!-- ... -->

    <DeployConfigFragment File="[TARGETDIR]\DeployConfig.xml" xmlns="http://www.deskwork.ru/2010/DeployConfig">
        <SolutionsList>
                <Solution Name="Softline.Portal.Helper" Id="21e394a2-7dab-4565-aaad-2540d360daf2" Require="true" Order="0" />
                <Solution Name="Softline.Portal.Libraries.MediaLibrary" Id="3b3ae4b1-26e7-4680-bd5c-db7e10de86e9" Require="true" Order="1">
                    <Features>
                        <Feature Name="Softline.Portal.Libraries.MediaLibrary.Softline_MediaLibrary" Id="fcc74650-a765-48af-91f6-247d50e7a907">
                            <ActivateRequire>true</ActivateRequire>
                        </Feature>
                    </Features>
                </Solution>
        </SolutionList>
    </DeployConfigFragment>

    <!-- ... -->

</Wix>

И все, что внутри <DeployConfigFragment>, хотелось бы чтобы в неизменном виде развертывалось в xml-файл DeployConfig.xml.

ДА! Я согласен, что это неправильно идеологически. Лучше разделять xml разного смысла, заключая inner xml в CDATA... Однако, в этом случае мы лишаемся интеллисенса, а этого бы очень не хотелось.

В общем, в итоге именно на таком варианте я и остановился, и приступил к исследованию возможности создания WiX Extensions.

Однако, в интернете оказалось не так уж и много документации по вопросу создания WiX Extension'ов, и поэтому мне пришлось лезть в исходники WiX и WiX Contrib Project.

В общем, этом посте я детально рассмотрел процесс создания CompilerExtension расширения для WiX.

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

Удобный способ просмотра свойств SPWeb

Некоторые вещи в рабочем проекте хранятся именно в SPWeb.Properties, и довольно часто возникает необходимость быстро посмотреть, что сейчас там лежит. Иногда - изменить какое-нибудь значение.

Раньше всегда использовал для этой цели консольные приложения (SharePoint Console Application), однако такой способ не всегда подходит: во-первых, у нас есть тестовые виртуалки, где студия не стоит; во-вторых, у клиентов опять же Visual Studio не установлена. Как быть?

Оказалось, ответ очень прост, и умещается в один скриншот:

SharePoint'овский PowerShell, напомню, доступен через меню Пуск. В целом, таким способом можно пробегать всю объектную модель SharePoint'а. Мне показалось очень полезным!

четверг, 20 января 2011 г.

Пустая ошибка SharePoint Designer

Столкнулись с интересной проблемой: у клиентов SharePoint Designer 2010 отказался соединяться с нашим порталом (SharePoint Foundation 2010). А сообщение об ошибке оказалось... пустым!

Симптомы (при попытке открытия сайта из SharePoint Designer):




По таким сообщениям об ошибке совершенно ничего не ясно, тем более, что сам портал прекрасно открывается через браузер и никаких ошибок не выдает.

Но если немножко поискать, в Windows Event Viewer можно обнаружить более подробное описание данной ошибки. Впрочем, тоже не слишком вразумительное:

Службе WebHost не удалось обработать запрос
Сведения об отправителе: System.ServiceModel.ServiceHostingEnvironment+HostingManager/32521977
Исключение: System.ServiceModel.ServiceActivationException: Не удается активировать запрошенную службу "/_vti_bin/client.svc" из-за исключения во время компиляции. Сообщение об исключении: Операция является недопустимой из-за текущего состояния объекта.. ---> System.InvalidOperationException: Операция является недопустимой из-за текущего состояния объекта.
в Microsoft.SharePoint.ApplicationRuntime.SPLayoutsMappedFile.MapLayoutsVirtualPath(String virtualPath)
в Microsoft.SharePoint.ApplicationRuntime.SPVirtualPathProvider.GetCacheKey(String virtualPath)
в System.Web.Compilation.BuildManager.GetCacheKeyFromVirtualPath(VirtualPath virtualPath, Boolean& keyFromVPP)
в System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile)
в System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile)
в System.Web.Compilation.BuildManager.GetCompiledCustomString(String virtualPath)
в System.ServiceModel.ServiceHostingEnvironment.HostingManager.GetCompiledCustomString(String normalizedVirtualPath)
в System.ServiceModel.ServiceHostingEnvironment.HostingManager.CreateService(String normalizedVirtualPath)
в System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(String normalizedVirtualPath)
в System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath)
--- Конец трассировки внутреннего стека исключений ---
в System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath)
в System.ServiceModel.ServiceHostingEnvironment.EnsureServiceAvailableFast(String relativeVirtualPath)
Имя процесса: w3wp

На системах с английской локалью сообщение выглядит следующим образом:


WebHost failed to process a request.
Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/17653682
Exception: System.ServiceModel.ServiceActivationException: The service '/_vti_bin/client.svc' cannot be activated due to an exception during compilation. The exception message is: Operation is not valid due to the current state of the object.. ---> System.InvalidOperationException: Operation is not valid due to the current state of the object.
at Microsoft.SharePoint.ApplicationRuntime.SPLayoutsMappedFile.MapLayoutsVirtualPath(String virtualPath)
at Microsoft.SharePoint.ApplicationRuntime.SPVirtualPathProvider.GetCacheKey(String virtualPath)
at Microsoft.SharePoint.Publishing.Internal.CmsVirtualPathProvider.GetCacheKey(String virtualPath)
at System.Web.Compilation.BuildManager.GetCacheKeyFromVirtualPath(VirtualPath virtualPath, Boolean& keyFromVPP)
at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile)
at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile)
at System.Web.Compilation.BuildManager.GetCompiledCustomString(String virtualPath)
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.GetCompiledCustomString(String normalizedVirtualPath)
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.CreateService(String normalizedVirtualPath)
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(String normalizedVirtualPath)
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath)
--- End of inner exception stack trace ---
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath)
at System.ServiceModel.ServiceHostingEnvironment.EnsureServiceAvailableFast(String relativeVirtualPath)
Process Name: w3wp


Ниже описано решение данной проблемы.