Поделиться через


Руководство. Использование асинхронных и хранимых процедур с 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 В методе и Delete HttpGet Edit методах 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);
}

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

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

<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

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

    Служба вставки отдела

Код сначала создает имена хранимых процедур по умолчанию. Если вы используете существующую базу данных, может потребоваться настроить имена хранимых процедур, чтобы использовать хранимые процедуры, уже определенные в базе данных. Дополнительные сведения о том, как это сделать, см. в разделе Entity Framework Code First Insert/Update/Delete Stored Procedures.

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

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

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

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

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

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

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

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

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

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

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

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

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

Следующие шаги

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

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

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