Обработка параллелизма с помощью Entity Framework 4.0 в веб-приложении ASP.NET 4

Том Дайкстра (Tom Dykstra)

Эта серия руководств основана на веб-приложении Университета Contoso, созданном с помощью начало работы с серией учебников Entity Framework 4.0. Если вы не выполнили предыдущие руководства, в качестве отправной точки для этого руководства вы можете скачать созданное приложение . Вы также можете скачать приложение , созданное в полной серии руководств. Если у вас есть вопросы по этим руководствам, вы можете опубликовать их на форуме ASP.NET Entity Framework.

В предыдущем руководстве вы узнали, как сортировать и фильтровать данные с помощью ObjectDataSource элемента управления и Entity Framework. В этом руководстве описаны варианты обработки параллелизма в веб-приложении ASP.NET, использующего Entity Framework. Вы создадите новую веб-страницу, посвященную обновлению заданий преподавателей в офисе. Вы будете обрабатывать проблемы параллелизма на этой странице и на странице Отделы, созданной ранее.

Image06

Изображение01

Конфликты параллелизма

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

Пессимистичный параллелизм (блокировка)

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

Управление блокировками имеет некоторые недостатки. Оно может оказаться сложным с точки зрения программирования. Для этого требуются значительные ресурсы для управления базами данных, и это может привести к проблемам с производительностью по мере увеличения числа пользователей приложения (т. е. недостаточного масштабирования). Поэтому не все системы управления базами данных поддерживают пессимистичный параллелизм. Платформа Entity Framework не предоставляет встроенной поддержки, и в этом руководстве не показано, как ее реализовать.

Оптимистическая блокировка

Альтернативой пессимистичному параллелизму является оптимистичный параллелизм. Оптимистическая блокировка допускает появление конфликтов параллелизма, а затем обрабатывает их соответствующим образом. Например, Джон запускает страницу Department.aspx , щелкает ссылку Изменить для отдела истории и уменьшает сумму бюджета с 1 000 000 долл. США до 125 000,00 долл. США. (Джон управляет конкурирующим отделом и хочет освободить деньги для своего собственного отдела.)

Image07

Прежде чем нажать кнопку Обновить, Джейн запускает ту же страницу, щелкает ссылку Изменить для отдела истории, а затем изменяет поле Дата начала с 10.01.2011 на 01.01.1999. (Джейн управляет отделом истории и хочет дать ему больше старшинства.)

Image08

Сначала Джон нажимает кнопку Обновить , а затем — Кнопку Обновить. Браузер Джейн теперь перечисляет сумму бюджета как $1,000,000.00, но это неправильно, поскольку сумма была изменена Джоном на $125,000.00.

Ниже перечислены некоторые действия, которые можно предпринять в этом сценарии.

  • Вы можете отслеживать, для какого свойства пользователь изменил и обновил только соответствующие столбцы в базе данных. В этом примере сценария данные не будут потеряны, так как эти два пользователя обновляли разные свойства. В следующий раз, когда кто-то просматривает отдел истории, он увидит 1/1/1999 и $ 125000.00.

    Это поведение по умолчанию в Entity Framework, которое может значительно сократить количество конфликтов, которые могут привести к потере данных. Однако такое поведение не позволяет избежать потери данных, если конкурирующие изменения вносятся в одно и то же свойство сущности. Кроме того, такое поведение не всегда возможно; При сопоставлении хранимых процедур с типом сущности все свойства сущности обновляются при внесении изменений в сущность в базе данных.

  • Вы можете позволить, чтобы изменения Джейн перезаписывали изменения Джона. После нажатия кнопки Обновить сумма бюджета возвращается к $1,000,000.00. Такой подход называется победой клиента или сохранением последнего внесенного изменения. (Клиентские значения имеют приоритет над тем, что находится в хранилище данных.)

  • Вы можете предотвратить обновление изменений Джейн в базе данных. Как правило, вы отображаете сообщение об ошибке, показываете текущее состояние данных и разрешаете повторно ввести изменения, если они по-прежнему нужны. Вы можете автоматизировать процесс, сохранив ее данные и предоставив ей возможность повторно применить его без необходимости повторного ввода. Это называется победой хранилища. (Значения в хранилище имеют приоритет над данными, передаваемыми клиентом.)

Обнаружение конфликтов параллелизма

В Entity Framework можно разрешать конфликты, обрабатывая OptimisticConcurrencyException исключения, создаваемые Entity Framework. Чтобы определить, когда именно нужно выдавать исключения, платформа Entity Framework должна быть в состоянии обнаруживать конфликты. Поэтому нужно соответствующим образом настроить базу данных и модель данных. Ниже приведены некоторые варианты для реализации обнаружения конфликтов:

  • Включите в базу данных столбец таблицы, который можно использовать для определения того, когда была изменена строка. Затем можно настроить Entity Framework для включения этого столбца в Where предложение SQL Update или Delete команд.

    Это назначение столбца Timestamp в OfficeAssignment таблице.

    Image09

    Тип данных столбца Timestamp также называется Timestamp. Однако столбец на самом деле не содержит значения даты или времени. Вместо этого значение представляет собой последовательное число, которое увеличивается при каждом обновлении строки. В команде UpdateWhere или Delete предложение включает исходное Timestamp значение. Если обновляемая строка была изменена другим пользователем, значение в Timestamp отличается от исходного значения, поэтому Where предложение не возвращает строку для обновления. Когда Entity Framework обнаруживает, что ни один из строк не был обновлен текущей Update командой или Delete (то есть, когда число затронутых строк равно нулю), она интерпретирует это как конфликт параллелизма.

  • Настройте Entity Framework для включения исходных значений каждого столбца в таблице в Where предложение Update команд и Delete .

    Как и в первом варианте, если что-либо в строке изменилось с момента первого чтения строки, Where предложение не вернет строку для обновления, которую Entity Framework интерпретирует как конфликт параллелизма. Этот метод так же эффективен, как и использование Timestamp поля, но может быть неэффективным. Для таблиц базы данных с большим количеством столбцов это может привести к созданию очень больших Where предложений, а в веб-приложении может потребоваться поддерживать большое количество состояний. Поддержание больших объемов состояния может повлиять на производительность приложения, так как ему либо требуются ресурсы сервера (например, состояние сеанса), либо они должны быть включены в саму веб-страницу (например, состояние просмотра).

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

Обработка оптимистичного параллелизма без свойства отслеживания

Чтобы реализовать оптимистичный параллелизм для сущности Department , у которой нет свойства отслеживания (Timestamp), необходимо выполнить следующие задачи:

  • Измените модель данных, чтобы включить отслеживание параллелизма для Department сущностей.
  • SchoolRepository В классе обработайте исключения параллелизма в методе SaveChanges .
  • На странице Departments.aspx обработайте исключения параллелизма, отображая сообщение для пользователя с предупреждением о неудачной попытке изменения. Затем пользователь может просмотреть текущие значения и повторить изменения, если они по-прежнему необходимы.

Включение отслеживания параллелизма в модели данных

В Visual Studio откройте веб-приложение Университета Contoso, с которым вы работали в предыдущем руководстве этой серии.

Откройте SchoolModel.edmx и в конструкторе моделей данных щелкните правой кнопкой мыши Name свойство сущности Department и выберите пункт Свойства. В окне Свойства измените свойство на ConcurrencyModeFixed.

Изображение16

Сделайте то же самое для других скалярных свойств, не относящихся к первичному ключу (Budget, StartDateи Administrator.) (Это невозможно сделать для свойств навигации.) Это указывает, что всякий раз, когда Entity Framework создает Update команду или Delete SQL для обновления Department сущности в базе данных, эти столбцы (с исходными значениями) должны быть включены в Where предложение . Если при выполнении Update команды или Delete строка не найдена, Entity Framework создаст исключение оптимистичного параллелизма.

Сохраните и закройте модель данных.

Обработка исключений параллелизма в DAL

Откройте файл SchoolRepository.cs и добавьте следующий using оператор для System.Data пространства имен:

using System.Data;

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

public void SaveChanges()
{
    try
    {
        context.SaveChanges();
    }
    catch (OptimisticConcurrencyException ocex)
    {
        context.Refresh(RefreshMode.StoreWins, ocex.StateEntries[0].Entity);
        throw ocex;
    }
}

Если при вызове этого метода возникает ошибка параллелизма, значения свойств сущности в памяти заменяются значениями, текущими в базе данных. Исключение параллелизма повторно создается, чтобы его обрабатывала веб-страница.

В методах DeleteDepartment и UpdateDepartment замените существующий вызов context.SaveChanges() на вызов SaveChanges() , чтобы вызвать новый метод .

Обработка исключений параллелизма на уровне представления

Откройте Файл Departments.aspx и добавьте OnDeleted="DepartmentsObjectDataSource_Deleted" атрибут в DepartmentsObjectDataSource элемент управления . Открывающий тег для элемента управления теперь будет выглядеть так, как показано в следующем примере.

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.BLL.SchoolBL" DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartmentsByName" DeleteMethod="DeleteDepartment" UpdateMethod="UpdateDepartment"
        ConflictDetection="CompareAllValues" OldValuesParameterFormatString="orig{0}" 
        OnUpdated="DepartmentsObjectDataSource_Updated" SortParameterName="sortExpression" 
        OnDeleted="DepartmentsObjectDataSource_Deleted" >

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

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" 
        DataKeyNames="DepartmentID,Name,Budget,StartDate,Administrator" 
        OnRowUpdating="DepartmentsGridView_RowUpdating"
        OnRowDataBound="DepartmentsGridView_RowDataBound"
        AllowSorting="True" >

Откройте файл Departments.aspx.cs и добавьте следующую using инструкцию System.Data для пространства имен:

using System.Data;

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

private void CheckForOptimisticConcurrencyException(ObjectDataSourceStatusEventArgs e, string function)
{
    if (e.Exception.InnerException is OptimisticConcurrencyException)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = 
            "The record you attempted to edit or delete was modified by another " +
            "user after you got the original value. The edit or delete operation was canceled " +
            "and the other user's values have been displayed so you can " +
            "determine whether you still want to edit or delete this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Этот код проверяет тип исключения, и если это исключение параллелизма, код динамически создает CustomValidator элемент управления, который, в свою очередь, отображает сообщение в элементе ValidationSummary управления .

Вызовите новый метод из добавленного Updated ранее обработчика событий. Кроме того, создайте новый Deleted обработчик событий, который вызывает тот же метод (но не выполняет никаких других действий):

protected void DepartmentsObjectDataSource_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        CheckForOptimisticConcurrencyException(e, "update");
        // ...
    }
}

protected void DepartmentsObjectDataSource_Deleted(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        CheckForOptimisticConcurrencyException(e, "delete");
    }
}

Тестирование оптимистичного параллелизма на странице "Отделы"

Запустите страницу Departments.aspx .

Снимок экрана: страница

Щелкните Изменить в строке и измените значение в столбце Бюджет . (Помните, что вы можете изменять только записи, созданные для этого руководства, так как существующие School записи базы данных содержат недопустимые данные. Запись для отдела экономики является безопасным, чтобы поэкспериментировать с.)

Изображение18

Откройте новое окно браузера и снова запустите страницу (скопируйте URL-адрес первого окна браузера во второе окно браузера).

Снимок экрана: новое окно браузера, готовое к вводу.

Щелкните Изменить в той же строке, что и ранее, и измените значение бюджета на другое.

Изображение19

Во втором окне браузера нажмите кнопку Обновить. Сумма бюджета успешно изменена на это новое значение.

Изображение 20

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

Изображение21

Обработка оптимистичного параллелизма с помощью свойства отслеживания

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

  • Добавьте хранимые процедуры в модель данных для управления сущностями OfficeAssignment . (Свойства отслеживания и хранимые процедуры не нужно использовать вместе. Они просто сгруппированы здесь для иллюстрации.)
  • Добавьте методы CRUD в DAL и BLL для сущностей, включая код для OfficeAssignment обработки исключений оптимистического параллелизма в DAL.
  • Создайте веб-страницу office-assignments.
  • Проверьте оптимистичный параллелизм на новой веб-странице.

Добавление хранимых процедур OfficeAssignment в модель данных

Откройте файл SchoolModel.edmx в конструкторе моделей, щелкните правой кнопкой мыши область конструктора и выберите команду Обновить модель из базы данных. На вкладке Добавить диалогового окна Выбор объектов базы данных разверните узел Хранимые процедуры и выберите три OfficeAssignment хранимые процедуры (см. следующий снимок экрана), а затем нажмите кнопку Готово. (Эти хранимые процедуры уже находились в базе данных при ее загрузке или создании с помощью скрипта.)

Изображение02

Щелкните правой кнопкой мыши сущность и выберите Stored Procedure Mapping (Сопоставление хранимыхOfficeAssignment процедур).

Изображение03

Задайте функции Вставка, Обновление и Удаление для использования соответствующих хранимых процедур. OrigTimestamp Для параметра Update функции присвойте свойству значение Timestamp и выберите параметр Использовать исходное значение.

Изображение04

Когда Entity Framework вызывает хранимую UpdateOfficeAssignment процедуру, она передает исходное значение столбца Timestamp в параметре OrigTimestamp . Хранимая процедура использует этот параметр в предложении Where :

ALTER PROCEDURE [dbo].[UpdateOfficeAssignment]
    @InstructorID int,
    @Location nvarchar(50),
    @OrigTimestamp timestamp
    AS
    UPDATE OfficeAssignment SET Location=@Location 
    WHERE InstructorID=@InstructorID AND [Timestamp]=@OrigTimestamp;
    IF @@ROWCOUNT > 0
    BEGIN
        SELECT [Timestamp] FROM OfficeAssignment 
            WHERE InstructorID=@InstructorID;
    END

Хранимая процедура также выбирает новое значение столбца Timestamp после обновления, чтобы платформа Entity Framework синхронизировать OfficeAssignment сущность, которая находится в памяти, с соответствующей строкой базы данных.

(Обратите внимание, что хранимая процедура удаления назначения office не имеет OrigTimestamp параметра. Из-за этого Entity Framework не может проверить, не изменилась ли сущность перед ее удалением.)

Сохраните и закройте модель данных.

Добавление методов OfficeAssignment в DAL

Откройте файл ISchoolRepository.cs и добавьте следующие методы CRUD для набора сущностей OfficeAssignment :

IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression);
void InsertOfficeAssignment(OfficeAssignment OfficeAssignment);
void DeleteOfficeAssignment(OfficeAssignment OfficeAssignment);
void UpdateOfficeAssignment(OfficeAssignment OfficeAssignment, OfficeAssignment origOfficeAssignment);

Добавьте следующие новые методы в Файл SchoolRepository.cs. В методе UpdateOfficeAssignment вызывается локальный SaveChanges метод вместо context.SaveChanges.

public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    return new ObjectQuery<OfficeAssignment>("SELECT VALUE o FROM OfficeAssignments AS o", context).Include("Person").OrderBy("it." + sortExpression).ToList();
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    context.OfficeAssignments.AddObject(officeAssignment);
    context.SaveChanges();
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    context.OfficeAssignments.Attach(officeAssignment);
    context.OfficeAssignments.DeleteObject(officeAssignment);
    context.SaveChanges();
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    context.OfficeAssignments.Attach(origOfficeAssignment);
    context.ApplyCurrentValues("OfficeAssignments", officeAssignment);
    SaveChanges();
}

В тестовом проекте откройте файл MockSchoolRepository.cs и добавьте в него следующую OfficeAssignment коллекцию и методы CRUD. (Макет репозитория должен реализовывать интерфейс репозитория, иначе решение не будет компилироваться.)

List<OfficeAssignment> officeAssignments = new List<OfficeAssignment>();
        
public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    return officeAssignments;
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    officeAssignments.Add(officeAssignment);
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    officeAssignments.Remove(officeAssignment);
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    officeAssignments.Remove(origOfficeAssignment);
    officeAssignments.Add(officeAssignment);
}

Добавление методов OfficeAssignment в BLL

В проекте main откройте файл SchoolBL.cs и добавьте следующие методы CRUD для заданной сущностиOfficeAssignment:

public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    if (string.IsNullOrEmpty(sortExpression)) sortExpression = "Person.LastName";
    return schoolRepository.GetOfficeAssignments(sortExpression);
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    try
    {
        schoolRepository.InsertOfficeAssignment(officeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    try
    {
        schoolRepository.DeleteOfficeAssignment(officeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    try
    {
        schoolRepository.UpdateOfficeAssignment(officeAssignment, origOfficeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

Создание веб-страницы OfficeAssignments

Создайте новую веб-страницу, которая использует страницу Site.Master master, и назовите ее OfficeAssignments.aspx. Добавьте следующую разметку в элемент управления с Content именем Content2:

<h2>Office Assignments</h2>
    <asp:ObjectDataSource ID="OfficeAssignmentsObjectDataSource" runat="server" TypeName="ContosoUniversity.BLL.SchoolBL"
        DataObjectTypeName="ContosoUniversity.DAL.OfficeAssignment" SelectMethod="GetOfficeAssignments"
        DeleteMethod="DeleteOfficeAssignment" UpdateMethod="UpdateOfficeAssignment" ConflictDetection="CompareAllValues"
        OldValuesParameterFormatString="orig{0}"
        SortParameterName="sortExpression"  OnUpdated="OfficeAssignmentsObjectDataSource_Updated">
    </asp:ObjectDataSource>
    <asp:ValidationSummary ID="OfficeAssignmentsValidationSummary" runat="server" ShowSummary="true"
        DisplayMode="BulletList" Style="color: Red; width: 40em;" />
    <asp:GridView ID="OfficeAssignmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="OfficeAssignmentsObjectDataSource" DataKeyNames="InstructorID,Timestamp"
        AllowSorting="True">
        <Columns>
            <asp:CommandField ShowEditButton="True" ShowDeleteButton="True" ItemStyle-VerticalAlign="Top">
                <ItemStyle VerticalAlign="Top"></ItemStyle>
            </asp:CommandField>
            <asp:TemplateField HeaderText="Instructor" SortExpression="Person.LastName">
                <ItemTemplate>
                    <asp:Label ID="InstructorLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>'></asp:Label>,
                    <asp:Label ID="InstructorFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:DynamicField DataField="Location" HeaderText="Location" SortExpression="Location"/>
        </Columns>
        <SelectedRowStyle BackColor="LightGray"></SelectedRowStyle>
    </asp:GridView>

Обратите внимание, что в атрибуте DataKeyNames разметка указывает Timestamp свойство, а также ключ записи (InstructorID). Указание свойств в атрибуте DataKeyNames приводит к тому, что элемент управления сохраняет их в состоянии элемента управления (аналогично состоянию представления), чтобы исходные значения были доступны во время обработки обратной передачи.

Если вы не сохранили Timestamp значение, платформа Entity Framework не будет содержать его для Where предложения команды SQL Update . Следовательно, ничего не будет найдено для обновления. В результате Entity Framework будет вызывать исключение оптимистичного параллелизма при каждом обновлении сущности OfficeAssignment .

Откройте файл OfficeAssignments.aspx.cs и добавьте следующую using инструкцию для уровня доступа к данным:

using ContosoUniversity.DAL;

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

protected void Page_Init(object sender, EventArgs e)
{
    OfficeAssignmentsGridView.EnableDynamicData(typeof(OfficeAssignment));
}

protected void OfficeAssignmentsObjectDataSource_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = "The record you attempted to " +
            "update has been modified by another user since you last visited this page. " +
            "Your update was canceled to allow you to review the other user's " +
            "changes and determine if you still want to update this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Тестирование оптимистичного параллелизма на странице OfficeAssignments

Запустите страницу OfficeAssignments.aspx .

Снимок экрана: страница

Щелкните Изменить в строке и измените значение в столбце Расположение .

Изображение11

Откройте новое окно браузера и снова запустите страницу (скопируйте URL-адрес из первого окна браузера во второе окно браузера).

Снимок экрана: новое окно браузера.

Щелкните Изменить в той же строке, что и ранее, и измените значение расположения на другое.

Изображение12

Во втором окне браузера нажмите кнопку Обновить.

Изображение13

Перейдите в первое окно браузера и нажмите кнопку Обновить.

Изображение15

Появится сообщение об ошибке, а значение Location было обновлено, чтобы отобразить значение, на которое вы его изменили во втором окне браузера.

Обработка параллелизма с помощью элемента управления EntityDataSource

Элемент EntityDataSource управления включает встроенную логику, которая распознает параметры параллелизма в модели данных и обрабатывает операции обновления и удаления соответствующим образом. Однако, как и во всех исключениях, необходимо обрабатывать OptimisticConcurrencyException исключения самостоятельно, чтобы предоставить понятное сообщение об ошибке.

Далее вы настроите страницу Courses.aspx (которая использует EntityDataSource элемент управления) для разрешения операций обновления и удаления и отображения сообщения об ошибке в случае конфликта параллелизма. У Course сущности нет столбца отслеживания параллелизма, поэтому вы будете использовать тот же метод, что и с сущностью Department : отслеживать значения всех неключевых свойств.

Откройте файл SchoolModel.edmx . Для неключевых свойств сущности Course (Title, Creditsи DepartmentID) задайте для свойства Режим параллелизма значение Fixed. Затем сохраните и закройте модель данных.

Откройте страницу Courses.aspx и внесите следующие изменения:

  • В элементе CoursesEntityDataSource управления добавьте EnableUpdate="true" атрибуты и EnableDelete="true" . Открывающий тег для этого элемента управления теперь похож на следующий пример:

    <asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false" 
            AutoGenerateWhereClause="True" EntitySetName="Courses"
            EnableUpdate="true" EnableDelete="true">
    
  • В элементе CoursesGridView управления измените значение атрибута DataKeyNames на "CourseID,Title,Credits,DepartmentID". Затем добавьте CommandField элемент в Columns элемент , который отображает кнопки Изменить и Удалить (<asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />). Элемент GridView управления теперь похож на следующий пример:

    <asp:GridView ID="CoursesGridView" runat="server" AutoGenerateColumns="False" 
            DataKeyNames="CourseID,Title,Credits,DepartmentID"
            DataSourceID="CoursesEntityDataSource" >
            <Columns>
                <asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />
                <asp:BoundField DataField="CourseID" HeaderText="CourseID" ReadOnly="True" SortExpression="CourseID" />
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                <asp:BoundField DataField="Credits" HeaderText="Credits" SortExpression="Credits" />
            </Columns>
        </asp:GridView>
    

Запустите страницу и создайте конфликтную ситуацию, как это было раньше на странице Отделы. Запустите страницу в двух окнах браузера, нажмите кнопку Изменить в одной строке в каждом окне и внесите разные изменения в каждое из них. Щелкните Обновить в одном окне, а затем — Обновить в другом. При повторном нажатии кнопки Обновить вы увидите страницу ошибки, которая возникает в результате необработанного исключения параллелизма.

Изображение 22

Эта ошибка обрабатывается так же, как и для ObjectDataSource элемента управления. Откройте страницу Courses.aspx и в элементе CoursesEntityDataSource управления укажите обработчики для Deleted событий и Updated . Открывающий тег элемента управления теперь похож на следующий пример:

<asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false"
        AutoGenerateWhereClause="true" EntitySetName="Courses" 
        EnableUpdate="true" EnableDelete="true" 
        OnDeleted="CoursesEntityDataSource_Deleted" 
        OnUpdated="CoursesEntityDataSource_Updated">

Перед элементом CoursesGridView управления добавьте следующий ValidationSummary элемент управления:

<asp:ValidationSummary ID="CoursesValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

В файле Courses.aspx.cs добавьте инструкцию using для System.Data пространства имен, добавьте метод, который проверяет наличие исключений параллелизма, и добавьте обработчики для EntityDataSource обработчиков элемента управления Updated и Deleted . Код будет выглядеть следующим образом:

using System.Data;
protected void CoursesEntityDataSource_Updated(object sender, EntityDataSourceChangedEventArgs e)
{
    CheckForOptimisticConcurrencyException(e, "update");
}

protected void CoursesEntityDataSource_Deleted(object sender, EntityDataSourceChangedEventArgs e)
{
    CheckForOptimisticConcurrencyException(e, "delete");
}

private void CheckForOptimisticConcurrencyException(EntityDataSourceChangedEventArgs e, string function)
{
    if (e.Exception != null && e.Exception is OptimisticConcurrencyException)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = 
            "The record you attempted to edit or delete was modified by another " +
            "user after you got the original value. The edit or delete operation was canceled " +
            "and the other user's values have been displayed so you can " +
            "determine whether you still want to edit or delete this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Единственное различие между этим кодом и тем, что вы сделали для ObjectDataSource элемента управления, заключается в том, что в этом случае исключение параллелизма находится в Exception свойстве объекта аргументов события, а не в свойстве InnerException этого исключения.

Запустите страницу и снова создайте конфликт параллелизма. На этот раз появится сообщение об ошибке:

Изображение23

На этом заканчивается введение в обработку конфликтов параллелизма. В следующем руководстве содержатся рекомендации по повышению производительности веб-приложения, использующего Entity Framework.