Руководство. Использование асинхронных и хранимых процедур с EF в приложении MVC ASP.NET

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

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

Наконец, вы повторно развернете приложение в Azure вместе со всеми изменениями базы данных, реализованными с момента первого развертывания.

На следующих рисунках изображены некоторые из страниц, с которыми вы будете работать.

Страница

Создание отдела

Изучив это руководство, вы:

  • Сведения об асинхронном коде
  • Создание контроллера отдела
  • Использование хранимых процедур
  • Развернуть в Azure

Предварительные требования

Зачем использовать асинхронный код

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

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

Дополнительные сведения об асинхронном программировании см. в статье Использование асинхронной поддержки .NET 4.5 во избежание блокировки вызовов.

Создание контроллера отдела

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

Ниже показано, что было добавлено в синхронный код метода, Index чтобы сделать его асинхронным.

public async Task<ActionResult> Index()
{
    var departments = db.Departments.Include(d => d.Administrator);
    return View(await departments.ToListAsync());
}

Для асинхронного выполнения запроса к базе данных Entity Framework были применены четыре изменения:

  • Метод помечается async ключевое слово, который указывает компилятору создавать обратные вызовы для частей тела метода и автоматически создавать Task<ActionResult> возвращаемый объект.
  • Тип возвращаемого значения изменен с ActionResult на Task<ActionResult>. Тип Task<T> представляет текущую работу с результатом типа T.
  • Ключевое слово await была применена к вызову веб-службы. Когда компилятор видит этот ключевое слово, он разделяет метод на две части. Первая часть завершается операцией, которая запускается асинхронно. Вторая часть помещается в метод обратного вызова, который вызывается по завершении операции.
  • Была вызвана ToList асинхронная версия метода расширения.

Почему изменяется оператор , departments.ToList а не departments = db.Departments оператор? Причина заключается в том, что асинхронно выполняются только инструкции, вызывающие отправку запросов или команд в базу данных. Инструкция departments = db.Departments настраивает запрос, но запрос не выполняется до ToList вызова метода . Поэтому асинхронно выполняется только ToList метод .

В методе Details иEditHttpGetи Delete метод является методом, Find который вызывает отправку запроса в базу данных, поэтому этот метод выполняется асинхронно:

public async Task<ActionResult> Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Department department = await db.Departments.FindAsync(id);
    if (department == null)
    {
        return HttpNotFound();
    }
    return View(department);
}

В методах Create, HttpPost Editи DeleteConfirmed вызывается SaveChanges вызов метода, который вызывает выполнение команды, а не инструкции, такие как db.Departments.Add(department) , которые вызывают изменение только сущностей в памяти.

public async Task<ActionResult> Create(Department department)
{
    if (ModelState.IsValid)
    {
        db.Departments.Add(department);
    await db.SaveChangesAsync();
        return RedirectToAction("Index");
    }

Откройте файл Views\Department\Index.cshtml и замените код шаблона следующим кодом:

@model IEnumerable<ContosoUniversity.Models.Department>
@{
    ViewBag.Title = "Departments";
}
<h2>Departments</h2>
<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Budget)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.StartDate)
        </th>
    <th>
            Administrator
        </th>
        <th></th>
    </tr>
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Budget)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.StartDate)
        </td>
    <td>
            @Html.DisplayFor(modelItem => item.Administrator.FullName)
            </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.DepartmentID }) |
            @Html.ActionLink("Details", "Details", new { id=item.DepartmentID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.DepartmentID })
        </td>
    </tr>
}
</table>

Этот код изменяет заголовок с Индекс на Отделы, перемещает имя администратора вправо и предоставляет полное имя администратора.

В представлениях Создание, Удаление, Сведения и Изменение измените подпись поля на InstructorID "Администратор" так же, как вы изменили поле названия отдела на "Отдел" в представлениях курса.

В представлениях Создание и Изменение используйте следующий код:

<label class="control-label col-md-2" for="InstructorID">Administrator</label>

В представлениях Удалить и Сведения используйте следующий код:

<dt>
    Administrator
</dt>

Запустите приложение и перейдите на вкладку Отделы .

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

При использовании асинхронного программирования с Entity Framework необходимо учитывать следующее:

  • Асинхронный код не является потокобезопасной. Иными словами, не пытайтесь выполнять несколько операций параллельно, используя один и тот же экземпляр контекста.
  • Если вы хотите использовать преимущества в производительности, которые обеспечивает асинхронный код, убедитесь, что все используемые пакеты библиотек (например, для разбиения на страницы) также используют асинхронный код при вызове любых методов Entity Framework, выполняющих запросы к базе данных.

Использование хранимых процедур

Некоторые разработчики и администраторы баз данных предпочитают использовать хранимые процедуры для доступа к базе данных. В более ранних версиях Entity Framework можно было получить данные с помощью хранимой процедуры, выполнив необработанный SQL-запрос, но вы не можете указать EF использовать хранимые процедуры для операций обновления. В EF 6 легко настроить Code First для использования хранимых процедур.

  1. В файле DAL\SchoolContext.cs добавьте выделенный код в OnModelCreating метод .

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        modelBuilder.Entity<Course>()
            .HasMany(c => c.Instructors).WithMany(i => i.Courses)
            .Map(t => t.MapLeftKey("CourseID")
                .MapRightKey("InstructorID")
                .ToTable("CourseInstructor"));
        modelBuilder.Entity<Department>().MapToStoredProcedures();
    }
    

    Этот код указывает Entity Framework использовать хранимые процедуры для операций вставки, обновления и удаления сущности Department .

  2. В консоли управления пакетами введите следующую команду:

    add-migration DepartmentSP

    Откройте файл Migrations\<timestamp>_DepartmentSP.cs , чтобы просмотреть код в методе Up , который создает хранимые процедуры вставки, обновления и удаления:

    public override void Up()
    {
        CreateStoredProcedure(
            "dbo.Department_Insert",
            p => new
                {
                    Name = p.String(maxLength: 50),
                    Budget = p.Decimal(precision: 19, scale: 4, storeType: "money"),
                    StartDate = p.DateTime(),
                    InstructorID = p.Int(),
                },
            body:
                @"INSERT [dbo].[Department]([Name], [Budget], [StartDate], [InstructorID])
                  VALUES (@Name, @Budget, @StartDate, @InstructorID)
                  
                  DECLARE @DepartmentID int
                  SELECT @DepartmentID = [DepartmentID]
                  FROM [dbo].[Department]
                  WHERE @@ROWCOUNT > 0 AND [DepartmentID] = scope_identity()
                  
                  SELECT t0.[DepartmentID]
                  FROM [dbo].[Department] AS t0
                  WHERE @@ROWCOUNT > 0 AND t0.[DepartmentID] = @DepartmentID"
        );
        
        CreateStoredProcedure(
            "dbo.Department_Update",
            p => new
                {
                    DepartmentID = p.Int(),
                    Name = p.String(maxLength: 50),
                    Budget = p.Decimal(precision: 19, scale: 4, storeType: "money"),
                    StartDate = p.DateTime(),
                    InstructorID = p.Int(),
                },
            body:
                @"UPDATE [dbo].[Department]
                  SET [Name] = @Name, [Budget] = @Budget, [StartDate] = @StartDate, [InstructorID] = @InstructorID
                  WHERE ([DepartmentID] = @DepartmentID)"
        );
        
        CreateStoredProcedure(
            "dbo.Department_Delete",
            p => new
                {
                    DepartmentID = p.Int(),
                },
            body:
                @"DELETE [dbo].[Department]
                  WHERE ([DepartmentID] = @DepartmentID)"
        );    
    }
    
  3. В консоли управления пакетами введите следующую команду:

    update-database

  4. Запустите приложение в режиме отладки, перейдите на вкладку Отделы и нажмите кнопку Создать.

  5. Введите данные для нового отдела и нажмите кнопку Создать.

  6. В Visual Studio просмотрите журналы в окне Вывод , чтобы увидеть, что для вставки новой строки Department использовалась хранимая процедура.

    Вставка пакета обновления для отдела

Code First создает имена хранимых процедур по умолчанию. Если вы используете существующую базу данных, может потребоваться настроить имена хранимых процедур, чтобы использовать хранимые процедуры, уже определенные в базе данных. Сведения о том, как это сделать, см. в статье Первая вставка, обновление и удаление хранимых процедур Entity Framework Code.

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

Если вы хотите изменить существующую хранимую процедуру, созданную во время предыдущей миграции, можно использовать команду Add-Migration, чтобы создать пустую миграцию, а затем вручную написать код, который вызывает метод AlterStoredProcedure .

Развернуть в Azure

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

  1. В Visual Studio щелкните правой кнопкой мыши проект в обозревателе решений и выберите Опубликовать в контекстном меню.

  2. Нажмите кнопку Опубликовать.

    Visual Studio развертывает приложение в Azure, и приложение откроется в браузере по умолчанию, работающем в Azure.

  3. Протестируйте приложение, чтобы убедиться, что оно работает.

    При первом запуске страницы, которая обращается к базе данных, Entity Framework выполняет все методы миграции, необходимые Up для обновления базы данных с текущей моделью данных. Теперь можно использовать все веб-страницы, добавленные с момента последнего развертывания, включая страницы отдела, добавленные в этом руководстве.

Получите код

Скачивание завершенного проекта

Дополнительные ресурсы

Ссылки на другие ресурсы Entity Framework можно найти в ASP.NET доступ к данным — рекомендуемые ресурсы.

Дальнейшие действия

Изучив это руководство, вы:

  • Сведения об асинхронном коде
  • Создание контроллера отдела
  • Используемые хранимые процедуры
  • Развертывание в Azure

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