- Список задач для проверки навыков.
- Создание задачи таймера (этот пост).
- Использование подходящих классов.
- Передача команд задаче таймера.
- Взаимодействие веб-фронтэнда с задачами таймера.
Ранее я приводил список задач для проверки навыков программирования для SharePoint. Сегодня напишу о решении четвертой задачи про задачу таймера для очистки библиотек документов от пустых папок.
Задача
Создать задачу таймера (Timer Job), которая буде находить в библиотеках документов пустые папки, в которых нет файлов и которые содержат пустые папки, и удалять их.
Класс задачи таймера
Чтобы создать задачу таймера необходимо создать класс, унаследованный от Microsoft.SharePoint.Administration.SPJobDefinition. Этот класс недоступен в sandbox, поэтому вам нужен farm solution.
В этом классе необходимо переопределить метод Execute и конструктор с параметрами.
public TimerJob(SPWebApplication webApp)
: base(Constants.TimerJobName, webApp, null,
SPJobLockType.ContentDatabase)
{
this.Title = "Folder cleanup job";
}
Первым параметром конструктора передается строка, которая будет отличать задачу таймера от других. Title задает отображаемое в админке имя задачи таймера.
Так как ферма SharePoint может состоять из нескольких серверов, то появляется интересный вопрос – где и сколько раз будет запускаться задача таймера. Особое внимание надо уделить параметру SPJobLockType. Детальное описание по ссылке. Возможные варианты:
- SPJobLockType.ContentDatabase - задача таймера запускается для каждой контентной базы данных родительского приложения.
- SPJobLockType.Job – задача таймера выполняется один раз на всю ферму. В данном режиме учитывается параметр SPServer конструктора, позволяющий указать конкретный сервер для запуска Timer Job.
- SPJobLockType.None – запускается на каждом сервере в ферме, где развернут родительский сервис. Очень полезно если вам надо запустить некоторый некоторый процесс на каждом сервере в ферме.
Добавление и удаление задачи таймера
Для добавления задачи таймера удобно использовать фичу уровня фермы или веб-приложения с флагом Activate On Default равным true. При разветрывании решения с такой фичей она автоматически активируется в указанной области действия.
Код feature receiver:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
var webApp = properties.Feature.Parent as SPWebApplication;
var job = new TimerJob(webApp);
job.Schedule = new SPHourlySchedule()
{
BeginMinute = 0,
EndMinute = 59
};
job.Update();
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
var webApp = properties.Feature.Parent as SPWebApplication;
var job = webApp.JobDefinitions.GetValue<TimerJob>(Constants.TimerJobName);
job.Delete();
}
Класс SPHourlySchedule является наследником SPSchedule и позволяет задавать расписание запуска задачи таймера.
Важно. Если вы попытаетесь добавить задачу таймера в фиче уровня Site или Web, то при деплое из Visual Studio оно сработает, а при попытке активировать фичу из веб-интерфейса упадет. Это новое ограничение SharePoint 2010, не позволяющее делать Update для классов наследников SPPersistedObject из контекста веб-приложения.
Если же у вас есть унаследованный код, который вы не можете поправить, то вам скорее всего пригодится эта статья.
Обработка элементов списков
Теперь перейдем к основной функции задачи таймера – очистка папок.
public override void Execute(Guid targetInstanceId)
{
SPWebApplication webApplication = this.Parent as SPWebApplication;
SPContentDatabase contentDb = webApplication.ContentDatabases[targetInstanceId];
ProcessDatabase(contentDb);
}
Так как при создании Timer Job был указан SPJobLockType.ContentDatabase, то в качестве параметра targetInstanceId будет ID базы данных контента.
Далее циклы по SPSite и SPWeb:
private void ProcessDatabase(SPContentDatabase contentDb)
{
foreach (SPSite site in contentDb.Sites)
{
try
{
ProcessSite(site);
}
finally
{
site.Dispose();
}
}
}
private void ProcessSite(SPSite site)
{
foreach (SPWeb web in site.AllWebs)
{
try
{
ProcessWeb(web);
}
finally
{
web.Dispose();
}
}
}
Вот такие конструкции необходимо использовать чтобы пройтись по всем SPSite и SPWeb в базе данных. Если не напишите Dispose в циклах, то на сервере очень быстро закончится память.
При разработке задач таймера надо быть очень аккуратным и освобождать все ресурсы. Для задач таймера почти не существует средств мониторинга потребления ресурсов, поэтому будет сложно определить кто поедает всю память.
Для того чтобы обезопасить себя и ваших пользователей необходимо использовать утилиту SPDisposeCheck. Она вам подскажет где надо совободить объекты.
Ну и наконец удаление папок:
private void ProcessWeb(SPWeb web)
{
foreach (var lib in web.Lists.OfType<SPDocumentLibrary>())
{
if (!lib.Hidden)
{
DeleteEmptyFolders(lib.RootFolder.SubFolders);
}
}
}
private void DeleteEmptyFolders(SPFolderCollection folders)
{
foreach (var folder in folders.OfType<SPFolder>().ToList())
{
DeleteEmptyFolder(folder);
}
}
private void DeleteEmptyFolder(SPFolder folder)
{
if (folder.Item != null)
{
DeleteEmptyFolders(folder.SubFolders);
if (folder.ItemCount == 0)
{
folder.Delete();
}
}
}
Не копипастите код из статьи до того как прочитаете следующую часть.
