Практическое руководство. Запись в базы данных контента с помощью LINQ to SharePoint

Дата последнего изменения: 9 марта 2015 г.

Применимо к: SharePoint Foundation 2010

В этой статье
Шаги 1 и 2: получение ссылок на веб-сайты и списки
Шаг 3: проверка включения отслеживания изменений объектов
Шаг 4: добавление базового кода для добавления, удаления, рециркуляции или обновления элемента списка
Шаг 5: добавление инфраструктуры для управления конфликтами параллельного доступа

В данном разделе поясняется, как создать код для поставщика LINQ to SharePoint, чтобы добавлять или удалять элементы списка из списков Microsoft SharePoint Foundation и изменять значения определенных полей в элементах списка.

Шаги 1 и 2: получение ссылок на веб-сайты и списки

Пояснение того, как в коде можно получить ссылку на веб-сайт и список, данные которых необходимо изменить, см. шаги 1 и 2 в разделе Практическое руководство. Выполнение запроса с помощью LINQ to SharePoint.

Шаг 3: проверка включения отслеживания изменений объектов

Чтобы LINQ to SharePoint можно было использовать для внесения изменений в базу данных, свойство ObjectTrackingEnabledдолжно иметь значение по умолчанию true. Если в коде для данного свойства устанавливается значение false и выполняется запрос объекта DataContext, то для свойства нельзя выполнить сброс в значение true. Таким образом, если код отправляет запрос для объекта DataContext и затем вносит изменения в базу данных контента, имеется два варианта:

  • Отказаться от установки значения false для свойства ObjectTrackingEnabled в коде.

  • Создать новый объект DataContext для того же веб-сайта в коде и разместить его после запросов, но перед выполнением записи в базу данных. При этом необходимо оставить для свойства ObjectTrackingEnabled нового объекта DataContext значение по умолчанию true и использовать новый объект для записи в базу данных контента.

Шаг 4: добавление базового кода для добавления, удаления, рециркуляции или обновления элемента списка

Базовый код для записи в базу данных контента очень прост. Все начинается с создания ссылки на веб-сайт и список и заканчивается вызовом SubmitChanges(). Посередине находится код, в котором вносятся необходимые изменения с помощью одного из методов *OnSubmit класса EntitySet<TEntity> или выполняется запись в поля списка с помощью обычного синтаксиса задания свойств. Во всех приведенных ниже примерах teamSite является объектом DataContext, который представляет веб-сайт, а TeamMembers является объектом EntitySet<TEntity>, который представляет список Участники групп.

Важное примечаниеВажно!

Объект, который представляет добавляемый, удаляемый, рециркулируемый или обновляемый элемент списка, должен иметь свойства EntityState, Id и Version. Это имеет место в том случае, если класс, представляющий тип контента, создается SPMetal. Различные методы *OnSubmit назначают значения свойству EntityState. Свойства Id и Version задаются средой выполнения SharePoint Foundation. В коде не должна выполняться запись ни в одно из этих трех свойств.

Добавление элемента списка

Чтобы добавить элемент в список, создайте объект того типа контента, к которому относится список, и передайте его в метод InsertOnSubmit(TEntity).

Важное примечаниеВажно!

Все свойства в этом объекте элемента списка, представляющие требуемые поля типа контента, должны иметь значение до того, как элемент будет вставлен в список. (Эти же свойства объявляются декоративным элементом [ColumnAttribute], у которого имеется свойство Required со значением true.) Например, все типы контента наследуют от базового типа контента ItemSharePoint Foundation, который имеет требуемое поле Title. Значения для таких свойств можно назначать между вызовами InsertOnSubmit(TEntity) и SubmitChanges(); но рекомендуется инициализировать требуемые свойства как можно скорее. Если отсутствует конструктор классов, который инициализирует все требуемые свойства, можно воспользоваться инициализатором объекта, как показано в приведенном ниже примере.

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

// Create the new list item.
TeamMember bob = new TeamMember() { Title="Bob Smith" };

// Set the item to be inserted.
teamSite.TeamMembers.InsertOnSubmit(bob);

// Write changes to the content database.
teamSite.SubmitChanges();

Для вставки нескольких элементов можно также использовать метод InsertAllOnSubmit(IEnumerable<TEntity>).

Удаление и рециркуляция элемента списка

В следующем примере показано, как использовать метод DeleteOnSubmit(TEntity) для удаления элемента из списка.

// Set the item to be deleted.
foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    if (teamMember.Title = "Bob Smith")
    {
        teamSite.TeamMembers.DeleteOnSubmit(teamMember);
    }
}

// Write changes to the content database.
teamSite.SubmitChanges();

Если необходимо не удалять элемент окончательно, а поместить его в корзину пользователя, используйте метод RecycleOnSubmit(TEntity). Можно также использовать методы DeleteAllOnSubmit(IEnumerable<TEntity>) и RecycleAllOnSubmit(IEnumerable<TEntity>) для одновременного удаления нескольких элементов.

Изменение поля в элементе списка

Чтобы изменить значение поля в элементе списка, просто выполните запись в свойство, которое представляет это поле, как показано в следующем примере:

// Set the property to a new value.
foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

// Write changes to the content database.
teamSite.SubmitChanges();

Внесение нескольких изменений с помощью одного вызова SubmitChanges

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

// ‘sally’ is a TeamMember object.
teamSite.TeamMembers.RecycleOnSubmit(sally);

// ‘leftCompany’ is an IList of TeamMember objects
teamSite.TeamMembers.DeleteAllOnSubmit(leftCompany);

foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

// Write changes to the content database.
teamSite.SubmitChanges();

Передаваемые изменения могут относиться к разным спискам. Изменения для одного или всех списков на веб-сайте можно передать с помощью одного вызова метода SubmitChanges().

Шаг 5: добавление инфраструктуры для управления конфликтами параллельного доступа

Если какие-либо из значений полей были изменены, то перед передачей изменений в базу данных контента с помощью вызова метода SubmitChanges() система отслеживания изменений объектов проверяет, не изменили ли другие пользователи любой из затронутых элементов списков после их получения из базы данных текущим пользовательским процессом. (Дополнительные сведения об этой системе см. в статье Отслеживание изменений объектов и оптимистический параллелизм.) Если имеет место конфликт параллельного доступа, SubmitChanges() создает исключение ChangeConflictException. Кроме того, создается объект MemberChangeConflict, который представляет сведения о несоответствиях. Если имеются другие несоответствия между текущим значением поля у клиента и значением поля в базе данных, каждый элемент также представляется объектом MemberChangeConflict. Все несоответствия для определенного элемента списка добавляются в свойство MemberConflicts объекта ObjectChangeConflict. В зависимости от того, какой перегруженный метод SubmitChanges() был вызван и какие параметры были в него переданы, может существовать сразу несколько объектов ObjectChangeConflict. Все они добавляются в свойство ChangeConflicts объекта DataContext.

Перед повторным вызовом SubmitChanges() код должен перехватить исключение и разрешить все несоответствия. В некоторых случаях наилучшее решение для устранения проблем заключается в том, чтобы предоставить пользователю возможность выбрать способ устранения возникшего несоответствия. Предоставляемый пользователю интерфейс можно заполнить данными из объектов ObjectChangeConflict в свойстве ChangeConflicts и из их дочерних объектов MemberChangeConflict; а именно OriginalValue, DatabaseValue и CurrentValue properties.

СоветСовет

В пользовательском интерфейсе рекомендуется четко разделить несоответствия, которые представляют действительные конфликты параллельного доступа, то есть несоответствия между свойствами OriginalValue и DatabaseValue, и несоответствия, просто представляющие изменения, которые были бы автоматически записаны в базу данных контента, если бы конфликты параллельного доступа отсутствовали, то есть несоответствия между DatabaseValue и CurrentValue, где OriginalValue = DatabaseValue.

Реализуйте предоставление выбора пользователю с помощью вызовов некоторых сочетаний следующих методов:

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

  • Какая версия значения поля должна сохраняться в базе данных контента — исходное значение, текущее значение в базе данных, значение в процессе приложения (значение клиента) или некоторое четвертое значение? Дополнительные сведения см. в описании RefreshMode и справочных материалах по методам из предыдущего списка. Кроме того, хотя статьи в следующей таблице посвящены поставщику LINQ to SQL, описываемая в них логическая схема применима и к LINQ to SharePoint.

    Практическое руководство: разрешение конфликтов параллельного доступа с помощью объединения со значениями базы данных (LINQ to SQL)

    Практическое руководство: разрешение конфликтов параллельного доступа с помощью сохранения значений базы данных (LINQ to SQL)

    Практическое руководство: разрешение конфликтов параллельного доступа с помощью перезаписи значений базы данных (LINQ to SQL)

  • Что необходимо предпринять, когда текущий пользователь передал изменение для элемента списка, который был окончательно удален из списка другим пользователем? Дополнительные сведения о возможных действиях см. в статьях Resolve(RefreshMode, Boolean) и ResolveAll(RefreshMode, Boolean).

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

    • Пропустить элементы списков, удаленные другим пользователем.

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

    Такую логическую схему можно применить с помощью простого вызова ChangeConflictCollection.ResolveAll().

    Однако если требуется сохранить все изменения элементов некоторых списков и при этом отменить изменения своего процесса в элементах других списков, то код должен в цикле проходить члены свойства ChangeConflicts, вызывая различные перегрузки ObjectChangeConflict.Resolve() и передавая различные параметры для разных элементов ObjectChangeConflict.

    Для некоторых типов контента может потребоваться применить различные правила для различных полей. В этом случае код должен в цикле проходить члены свойства MemberConflicts некоторых объектов ObjectChangeConflict, вызывая различные перегрузки MemberChangeConflict.Resolve() и передавая различные параметры для разных полей.

  • Когда требуется создать исключение ChangeConflictException и остановить внесение дальнейших изменений? Это является частью логической схемы разрешения, хотя решение реализуется с помощью вызова SubmitChanges(). Существует два варианта: можно создать исключение сразу же после обнаружения конфликта параллельного доступа или можно продолжить запись всех ожидающих изменений. В последнем случае исключение создается (а изменения полей, для которых возник конфликт, отменяются) при обнаружении одного или нескольких конфликтов параллельного доступа. Одно из преимуществ второго варианта заключается в том, что свойство MemberConflicts содержит полную детализацию всех конфликтов параллельного доступа (и других несоответствий) для всего набора переданных изменений. Таким образом, логическая схема разрешения позволяет обрабатывать весь набор. Вы задаете, какой из вариантов используется определенной перегрузкой вызываемого метода SubmitChanges() и какие параметры в него передаются.

В следующем коде представлен простейший способ разрешения всех несоответствий.

foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

try 
{
    teamSite.SubmitChanges();
}
catch (ChangeConflictException e) 
{
    teamSite.ChangeConflicts.ResolveAll();
    teamSite.SubmitChanges();
}

Первый вызов SubmitChanges() представляет собой перегрузку без параметров. Он создает исключение сразу после обнаружения первого конфликта параллельного доступа. Таким образом, вызов ChangeConflictCollection.ResolveAll() разрешает только все несоответствия, зарегистрированные до этого момента. Если в переданных изменениях содержится еще один конфликт параллельного доступа, он приводит ко второму вызову SubmitChanges() для создания исключения.

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

foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

try 
{
    teamSite.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException e) 
{
    foreach (ObjectChangeConflict changedListItem in teamSite.ChangeConflicts)
    {
        // If another user has changed properties of a non-manager,
        // leave that other user’s changes, except for the TopTask field.
        if (((TeamMember)changedListItem.Object).IsManager = false)
        {        
             foreach (MemberChangeConflict changedField in changedListItem.MemberConflicts)
            {
                if (changedField.Member.Name == "TopTask")
                {
                    changedField.Resolve(RefreshMode.KeepCurrentValues);
                }
                else
                {
                    changedField.Resolve(RefreshMode.OverwriteCurrentValues);
                }
            }
        }
        // But if another user has changed properties of a manager, let this
        // process’s changes override the other user’s changes.
        else
        {
            changedListItem.Resolve(RefreshMode.KeepCurrentValues);
        }    
    }

    teamSite.SubmitChanges();
} // end catch