Один из подходов к отладке решений для SharePoint

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

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

Стандартный процесс включения отладки и сам процесс следующий:

  1. Включение отладки в web.config
    • CallStack="true"
    • customErrors mode="Off"
    • compilation debug="true"
  2. Процесс отладки
    • Подцепление к процессам w3wp.exe/SPUCWorkerProcess.exe/OWSTimer.exe;
    • Установка требуемых точек останова;
    • Выполнение требуемой операции и пошаговая отладка кода.

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

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

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

  1. Создаем новый проект по шаблону SharePoint 2013 – Empty Project и назовем его SharePointApplication:

  1. Укажем тип решения (Farm solution. Вы же знаете в чем отличие Farm от Sandbox Solution? Сейчас не будем вникать в подробности. Необходимо стараться реализовывать Sandbox Solutions, но об этом в другой статье) и укажем адрес нашей сайт-коллекции в SharePoint:

  1. Далее создадим для примера простенький и без особого смысла класс DEMO с методом расширения класса SPWeb, который будет отвечать за обновление отфильтрованных списков данного узла в части свойства, отвечающего за открытие форм списка в модальном окне или же нет:
    • Добавим новый класс в проект и назовем его Demo.cs:

    • Внутри класса вставим следующий код:
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using Microsoft.SharePoint;
      
      namespace SharePointApplication
      {
          static public class Demo
          {
      
              public static void UpdateNoDialog(this SPWeb Web, string Filter)
              {
                  var Lists = new List();
      
                  if (!String.IsNullOrEmpty(Filter))
                  {
                      Lists = Web.Lists.Cast()
                                .Where(x => x.Title.ToLower().IndexOf(Filter.ToLower(), System.StringComparison.Ordinal) != -1)
                                .ToList();
                  }
      
                  Lists.ForEach(x =>
                  {
                      x.NavigateForFormsPages = true;
                      x.Update();
                  });
              }
          }
      }
      
    • В результате получится следующая картина:

  2. Создадим веб-часть с полем ввода фильтра и кнопкой вызова данного метода:
    • Добавим новую веб-часть в проект и назовем ее DemoWebPart

    • В файле Elements.xml, связанном с данной веб-частью, необходимо указать название группы (DemoGroup), в которой будет отображаться созданная веб-часть:
      <?xml version="1.0" encoding="utf-8"?>
      <Elements xmlns="http://schemas.microsoft.com/sharepoint/" >
        <Module Name="DemoWebPart" List="113" Url="_catalogs/wp">
          <File Path="DemoWebPartDemoWebPart.webpart" Url="SharePointApplication_DemoWebPart.webpart" Type="GhostableInLibrary" >
            <Property Name="Group" Value="DemoGroup" />
          </File>
        </Module>
      </Elements>
      
    • В файле DemoWebPart.webpart необходимо указать отображаемый заголовок и описание веб-части:
      <?xml version="1.0" encoding="utf-8"?>
      
      <webParts>
      
        <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
      
          <metaData>
      
            <type name="SharePointApplication.DemoWebPart.DemoWebPart, $SharePoint.Project.AssemblyFullName$" />
      
            <importErrorMessage>$Resources:core,ImportErrorMessage;</importErrorMessage>
      
          </metaData>
      
          <data>
      
            <properties>
      
              <property name="Title" type="string">Демонстрационная веб-часть</property>
      
              <property name="Description" type="string">Веб-часть для обновления списков по фильтру</property>
      
            </properties>
      
          </data>
      
        </webPart>
      
      </webParts>
      
    • В файле DemoWebPart.ascx необходимо прописать следующий код:
      <%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
      
      <%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 
      
      <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 
      
      <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
      
      <%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
      
      <%@ Import Namespace="Microsoft.SharePoint" %> 
      
      <%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
      
      <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="DemoWebPart.ascx.cs" Inherits="SharePointApplication.DemoWebPart.DemoWebPart" %>
      
      <asp:Label ID="ResultLabel" runat="server" Text="Label" Visible="False" ForeColor="Green"></asp:Label><br/>
      
      <asp:TextBox ID="FilterBox" runat="server"></asp:TextBox><br/>
      
      <asp:Button ID="ExecuteButton" runat="server" Text="Запустить" OnClick="ExecuteButton_OnClick"/>
      
    • В файле DemoWebPart.ascx.cs необходимо добавить функцию:
      protected void ExecuteButton_OnClick(object sender, EventArgs e)
      {
      	try
      	{
      		SPContext.Current.Web.UpdateNoDialog(FilterBox.Text);
      		ResultLabel.Text = DateTime.Now.ToString()+" Завершено!";
      		ResultLabel.Visible = true;
      		ResultLabel.ForeColor = System.Drawing.Color.Green;
      	}
      	catch (Exception Error)
      	{
      		//Тут мы еще должны записать ошибку в лог.
      
      		ResultLabel.Text = DateTime.Now.ToString() + " Произошла ошибка! " + Error.Message;
      		ResultLabel.Visible = true;
      		ResultLabel.ForeColor = System.Drawing.Color.Red;                
      	}
      }
      
  3. Развернем решение на нашем сайте

  1. Разместим требуемую веб-часть на странице и проверим работоспособность.
    • Перейти на страницу, где планируем разместить веб-часть

    • В риббоне нажимаем Страница – Изменить:

    • В измененном виде выбираем Вставка – ВебЧасть – DemoGroup – Демонстрационная веб-часть и кнопку Добавить:

       

    • Указываем значение фильтра (MyLists, например) и нажимаем кнопку Запустить:

  2. Теперь мы понимаем, что если бы что-то пошло не так и необходимо было искать причину проблемы, необходимо подключать процесс w3wp и следить за происходящим, вносить изменения в код, снова деплоить, проверять и так далее. Основную часть проверок (как минимум бизнес-логики) можно проверить в консольном приложении.
  3. Добавляем к нашему решению новое консольное приложение и назовем его ExecuteApplication.

  1. Изменяем режим запуска приложения на платформу x64.
    • Заходим в свойства проекта:

    • Переходим на вкладку Build и в поле Perform target укажите x64:

  2. Устанавливаем консольное приложение проектом для запуска:

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

 

  1. В основном методе консольного приложения создаем подключение к нашему узлу SharePoint, в контексте которого будет работать и вызываем необходимую функцию, которую хотим проверить. Примерный код следующий:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Microsoft.SharePoint;
    using SharePointApplication;
    
    namespace ExecuteApplication
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (var Site = new SPSite("http://cib_dev3/a/snitko/"))
                {
                    using (var Web = Site.RootWeb)
                    {
                        Web.UpdateNoDialog("MyLists");
                    }
                }
            }
        }
    }
    
  1. Запускаем проект и следим за происходящим в режиме реального времени:

  1. Если видим следующую ошибку (Microsoft SharePoint не поддерживается в 32-разрядном процессе. Используйте 64-разрядный исполняемый файл.), значит мы, скорее всего, не выполнили пункт номер 9. Иногда ошибка может выглядеть иначе (например, что сайт недоступен), но суть ошибки такая же:

  1. Если же после изменений в классе и очередном запуске проекта видим следующую ошибку (Метод не найден: "Void SharePointApplication.Demo.UpdateNoDialogNew(Microsoft.SharePoint.SPWeb, System.String)".),:

то скорее всего она связана с тем, что сборка из соседнего решения существует как локально (в проекте), так и в GAC, но в GAC сборка более старая, но при запуске более приоритетная. Таким образом, получается, что при написание кода студия ошибок не выдает, потому как использует сборку локальную, а при запуске решения сообщает о том, что в сборке (которая в GAC) нет методов, которые Вы вызываете. Для этого необходимо перед запуском консольного приложения положить в GAC измененную библиотеку соседнего проекта.

  • Необходимо найти gacutil.exe (архив приложен к статье), создать папку в файловой системе и расположить содержимое архива в этой папке. Например, C:gacutil.
  • Переходим в проект SharePointApplication (с основной бизнес-логикой), заходим в свойства проекта на вкладку Build Events:

  • В поле Post Build Event прописываем следующий код:
C:gacutilgacutil.exe" /i "$(TargetPath)

  • Получилось, что на событие “После построения проекта”, которые вызывается при запуске проекта, добавили инсталляцию итоговой библиотеки текущего проекта (она находится по пути $(TargetPath) – это константа Visual Studio, которая сама формирует требуемый  путь)  в GAC с помощью утилиты gacutil.
  • В результате ошибки больше нет и при запуске консольного приложения.

Поздравляю! Процесс отладки основной бизнес-логики теперь работает на Ура.

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


Опубликовано: 21.03.2014
Автор: Сергей Снитко