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

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

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

Наконец, вы повторно развертываете приложение в 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 и HttpGet Edit и 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");
    }

Откройте виевс\департмент\индекс.кштмли замените код шаблона следующим кодом:

@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>

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

В представлениях создание, удаление, сведения и изменение измените заголовок поля InstructorID на "Администратор" так же, как и в поле "название отдела" в представлениях курсов.

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

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

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

<dt>
    Administrator
</dt>

Запустите приложение и перейдите на вкладку Departments (подразделения ).

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

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

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

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

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

  1. В дал\счулконтекст.КСДобавьте выделенный код в метод 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

    Откройте миграцию\<метку времени>_DepartmentSP.CS , чтобы увидеть код в методе Up, который создает хранимые процедуры INSERT, Update и DELETE:

    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 просмотрите журналы в окне вывод , чтобы увидеть, что для вставки новой строки отдела использовалась хранимая процедура.

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

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

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

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

Развертывание в Azure

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

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

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

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

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

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

Получение кода

Скачать завершенный проект

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

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

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

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

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

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