Linq to SharePoint. Блокировка документов

Иногда при работе с документами необходимо на некоторое время запретить другим пользователями его изменение. В Microsoft SharePoint 2010 для реализации данного требования есть две возможности: извлечение файла и блокировка файла. В этом посте я расскажу об отличиях этих двух операций и покажу пример реализации блокировки документов с использованием репозитория.

Извлечение файлов

Первый способ, извлечение файлов, доступен из интерфейса пользователя. Это стандартная реализация системы управления версиями. Есть один недостаток: пользователи с достаточными на то привилегиями могут отменить извлечение документа или вернуть его. SharePoint в таком случае просто выдаст предупреждение о том, что файл был извлечен другим пользователем:

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

Блокировка документов

Второй способ более радикален и позволяет обеспечить монопольный доступ к файлу. Но здесь есть несколько особенностей:

  • Заблокировать файл, используя интерфейс пользователя нельзя;
  • Блокировать файл можно только на определенное время. Это связано с тем, что блокировку может отменить только пользователь, наложивший её;
  • При попытке редактировать свойства заблокированного файл, возникает исключение, которое не обрабатывается SharePoint'ом в дружелюбном виде.

Исключение это выглядит следующим образом:

<nativehr>0x80071779</nativehr><nativestack></nativestack>  
Файл "http://[SPServer]/DocumentLibrary/Report.xlsx" заблокирован для монопольного использования пользователем SHAREPOINT\system.

Или так (по-английски):

<nativehr>0x80071779</nativehr><nativestack></nativestack>
The file "http://[SPServer]/DocumentLibrary/Report.xlsx" is locked for exclusive use by SHAREPOINT\system.

При открытии файла в клиентском приложении SharePoint выдаст вот такое сообщение:

Блокировка файла программными средствами

Так как блокировать документы в SharePoint'е можно только программно, я покажу как это сделать на примере репозитория, описанного в посте Linq to SharePoint. Паттерн Repository

Блокировка в объектной модели SharePoint

В объектной модели SharePoint блокировка реализована в классе SPFile и представлена следующими методами:

  • Lock(SPLockType lockType, string lockId, TimeSpan timeout) - блокирование файла на заданный период времени;
  • ** RefreshLock(string lockId, TimeSpan timeout)** - продление блокировки файла на заданный период времени;
  • ReleaseLock(string lockId) - снятие блокировки с файла;

Информация о блокировке файла содержится в следующих свойствах объекта SPFile:

  • LockedByUser - пользователь, заблокировавший файл;
  • LockedDate - дата блокировки файла;
  • LockExpires - время, на которое заблокирован файл;
  • LockId - Id блокировки;
  • LockType - тип блокировки.;

Тип блокировки может принимать следующие значения:

  • Exclusive - доступ к файлу имеет только пользователь, наложивший на него блокировку;
  • Shared - доступ к файлу имеет группа пользователей;
  • None - файл не заблокирован;

Получение файла

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

/// <summary>
/// Получение файла элемента
/// </summary>
/// <param name="id">Id элемента</param>
/// <returns>Файл</returns>
protected SPFile GetFile(int id)
{
    // Получаем элемент
    var entity = GetEntity(id);
    // Получаем список, содержащий элемент
    var list = MetaData.List;
    // Получаем сайт, содержащий список
    var web = list.ParentWeb;
    // Получаем файл
    var file = web.GetFile(entity.ServerUrl);
    return file;
}
C#

Этот метод использует мета-данные списка, способ получение которых я описал в посте Linq to SharePoint. Получение мета-данных списка. В SharePoint'е и для элемента списка можно получить файл, но заблокировать его не удастся, т.к. он не является файлом, подлежащим хранению в системе управления версиями.

Блокировка файла

Файл есть, теперь можно и блокировку реализовать:

/// <summary>
/// Блокировка элемента
/// </summary>
/// <param name="id">Id элемента</param>
/// <param name="lockType">Тип блокировка</param>
/// <param name="timeSpan">Тайм-аут</param>
public void LockEntity(int id, SPFile.SPLockType lockType, TimeSpan timeSpan)
{
    var file = GetFile(id);
    if (file.ListItemAllFields.ParentList is SPDocumentLibrary)
    {
        if (file.LockType == SPFile.SPLockType.None)
        {
            file.Lock(lockType, Guid.NewGuid().ToString("N"), timeSpan);
        }
    }
}
 
/// <summary>
/// Снятие блокировки
/// </summary>
/// <param name="id">Id элемента</param>
public void ReleaseLockEntity(int id)
{
    var file = GetFile(id);
    if (file.LockType != SPFile.SPLockType.None)
    {
        file.ReleaseLock(file.LockId);
    }
}
 
/// <summary>
/// Продление блокировки
/// </summary>
/// <param name="id">Id элемента</param>
/// <param name="timeSpan">Тайм-аут</param>
public void RefreshLockEntity(int id, TimeSpan timeSpan)
{
    string lockId;
    var file = GetFile(id);
    if (file.LockType != SPFile.SPLockType.None)
    {
        file.RefreshLock(file.LockId, timeSpan);
    }
}
C#

В объектной модели SharePoint при блокировки файла или её продления требуется указать Id этой блокировки. В реализации этих методов в репозитории я "опустил" это требование.

Информация о блокировке

Для получения информации о блокировке файла я сделал простой класс:

public class EntityLockInfo
{
    public SPFile.SPLockType LockType { get; set; }
    public string LockId { get; set; }
    public DateTime LockExpires { get; set; }
    public DateTime LockedDate { get; set; }
    public SPUser LockedByUser { get; set; }
 
    public EntityLockInfo(SPFile file)
    {
        LockType = file.LockType;
        LockId = file.LockId;
        LockExpires = file.LockExpires;
        LockedByUser = file.LockedByUser;
        LockedDate = file.LockedDate;
    }
}
C#

И метод в репозитории для получения его экземпляра:

/// <summary>
/// Получение информации о блокировке элемента
/// </summary>
/// <param name="id">Id элемента</param>
public EntityLockInfo GetLockInfo(int id)
{
    var file = GetFile(id);
    var info = new EntityLockInfo(file);
    return info;
}
C#

Применение

Использовать новый функционал можно примерно так:

var repository = new DocumentRepository(siteUrl, false);
repository.LockEntity(1, SPFile.SPLockType.Exclusive, TimeSpan.FromHours(10));
C#
Виталий Жуков

Виталий Жуков

Техлид, Архитектор, Разработчик, Microsoft MVP. Более 20 лет опыта в области системной интеграции и разработки программного обеспечения. Специализируюсь на проектировании и внедрении масштабируемых высокопроизводительных программных решений в различных отраслях.

Смотрите также