1. Список задач для проверки навыков.
  2. Создание задачи таймера.
  3. Использование подходящих классов (этот пост).
  4. Передача команд задаче таймера.
  5. Взаимодействие веб-фронтэнда с задачами таймера.

В прошлый раз я писал о том как создать задачу таймера в SharePoint. Такой код писать не нужно. Надеюсь никто не успел скопипастить код к себе в проект.

В SharePoint 2010 основной класс для создания задач таймера – SPPausableJobDefinition. В отличии от обычного SPJobDefinition, как вы можете догадаться из названия, SPPausableJobDefinition можно останавливать.

Чтобы создать приостанавливаемую задачу таймера надо переопределить метод Execute с параметром SPJobState. Объект класса SPJobState содержат как свойства, говорящие о том что задача должна остановиться, так и методы сохранения текущего состояния задачи таймера для продолжения работы.

С одной стороны возможность приостанавливать выполнение задачи таймера ведет только к усложнению кода, но нету необходимости писать код, он уже есть в сборке Microsoft.SharePoint.

Класс, который больше всего подходит к нашей задаче – SPAllSitesJobDefinition. Задачи таймера, наследующиеся от этого класса должны переопределить только один метод ProcessSite.

public class TimerJob : SPAllSitesJobDefinition
{
    public TimerJob()
        : base()
    {

    }

    public TimerJob(SPWebApplication webApp)
        : base(Constants.TimerJobName, webApp)
    {
        this.Title = "Folder cleanup job";
    }

    public override void ProcessSite(SPSite site, SPJobState jobState)
    {            
        foreach (SPWeb web in site.AllWebs)
        {
            try
            {
                ProcessWeb(web);
            }
            finally
            {
                web.Dispose();
            }
        }
    }

    private void ProcessWeb(SPWeb web)
    {
        //omited for clarity
    }
}

Кода получилось даже меньше чем в первом варианте, при этом он поддерживает приостановку и запуск с места остановки, а также обновляет значения прогресса выполнения (это новая фича SharePoint 2010).

В прошлом посте писал о выборе значения SPJobLockType. В SharePoint 2010 нет такой необходимости. Разные варианты запуска задач таймера реализованы разными классами в сборке Microsoft.SharePoint.
Например: SPContentDatabaseJobDefinition, SPFirstAvailableServiceJobDefinition, SPServerJobDefinition, SPServiceJobDefinition.

Далее можно модифицировать код ProcessSite и ProcessWeb чтобы поддерживать перезапуск задачи с конкретной библиотеки документов. Но если вы разрабатываете для SharePoint Server 2010 (платной версии), то вам и это не надо делать. Код уже написан.

TimerJobUtility

TimerJobUtility – класс из сборки Microsoft.Office.Server. Он позволяет обходить содержимое SharePoint учитывая возможность остановки и перезапуска задачи таймера.

С использованием TimerJobUtility методы будут выглядеть так:

TimerJobUtility tju;
public override void ProcessSite(SPSite site, SPJobState jobState)
{
    tju = new TimerJobUtility(Constants.TimerJobName, jobState);
    tju.DisableEventFiring = false;
    tju.CancellationGranularity = IterationGranularity.List;
    tju.ResumeGranularity = IterationGranularity.List;
    tju.ProcessSite(site, s => tju.ProcessSite(s, ProcessWeb, null));
}

private void ProcessWeb(SPWeb web)
{
    tju.ProcessLists(web.Lists, ProcessList, null);
}

private void ProcessList(SPList list)
{
    if (!list.Hidden && list is SPDocumentLibrary)
    {
        DeleteEmptyFolders(list.RootFolder.SubFolders);
    }
}

private void DeleteEmptyFolders(SPFolderCollection folders)
{
    //omited for clarity
}

С таким кодом в случае остановки и перезапуска задачи она продолжит выполнять начиная с того списка, который не был обработан. Кроме того есть возможность блокировать срабатывание обработчиков событий установкой флага DisableEventFiring.

SPFolderHierarchy

5000… Для всех разработчиков SharePoint 2010 это магическая цифра. Если у вас запрос должен обработать более 5000 строк, то выпадает SPQueryThrottledException. Причем даже если реально будет возвращено мало строк, но для их вычисления придется просмотреть более 5000 элементов, то будет ошибка. В таких случаях помогает индекс или постраничное разбиение.

Нетрудно догадаться что стандартная реализация свойства SubFolders считывает все элементы из папки, которых может оказаться более 5000., что вызовет ошибку. Чтобы обрабатывать такие случаи в SharePoint Server 2010 (платной версии) есть класс SPFolderHierarchy, который помогает избежать проблем и содержит множество эвристик для максимального быстродействия навигации по папкам.

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

public class TimerJob : SPAllSitesJobDefinition
{
    TimerJobUtility tju;

    public TimerJob(): base() { }

    public TimerJob(SPWebApplication webApp)
        : base(Constants.TimerJobName, webApp)
    {
        this.Title = "Folder cleanup job";
    }

    public override void ProcessSite(SPSite site, SPJobState jobState)
    {
        tju = new TimerJobUtility(Constants.TimerJobName, jobState);
        tju.DisableEventFiring = false;
        tju.CancellationGranularity = IterationGranularity.List;
        tju.ResumeGranularity = IterationGranularity.List;
        tju.ProcessSite(site, s => tju.ProcessSite(s, ProcessWeb, null));
    }

    private void ProcessWeb(SPWeb web)
    {
        tju.ProcessLists(web.Lists, ProcessList, null);
    }

    private void ProcessList(SPList list)
    {
        if (!list.Hidden && list is SPDocumentLibrary)
        {
            DeleteEmptyFolders(new SPFolderHierarchy(list));
        }
    }

    private void DeleteEmptyFolders(SPFolderHierarchy h)
    {
        foreach (SPFolder folder in (h as IEnumerable<SPFolder>))
        {
            if (folder.Item != null)
            {
                DeleteEmptyFolders(h.GetSubFolders(folder.ServerRelativeUrl));
                if (folder.ItemCount == 0)
                {
                    folder.Delete();
                }
            }
        }
    }
}

Кроме повышенной надежности такого кода есть еще одно преимущество: классы TimerJobUtility и SPFolderHierarchy очень активно пишут диагностические сообщения в ULS. Таким образом вам гораздо будет легче отлаживать такой код на удаленной машине.

Теги : SharePoint