Руководство. Использование асинхронных и хранимых процедур с 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);
}
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>
В представлениях Delete и Details используйте следующий код:
<dt>
Administrator
</dt>
Запустите приложение и перейдите на вкладку "Отделы ".
Все работает так же, как и в других контроллерах, но в этом контроллере все запросы SQL выполняются асинхронно.
Некоторые моменты, которые следует учитывать при использовании асинхронного программирования с entity Framework:
- Асинхронный код не является потокобезопасной. Другими словами, не пытайтесь выполнять несколько операций параллельно с использованием одного экземпляра контекста.
- Если вы хотите использовать преимущества в производительности, которые обеспечивает асинхронный код, убедитесь, что все используемые пакеты библиотек (например, для разбиения на страницы) также используют асинхронный код при вызове любых методов Entity Framework, выполняющих запросы к базе данных.
Использование хранимых процедур
Некоторые разработчики и базы данных предпочитают использовать хранимые процедуры для доступа к базе данных. В более ранних версиях Entity Framework можно получить данные с помощью хранимой процедуры, выполнив необработанный SQL-запрос, но вы не можете указать EF использовать хранимые процедуры для операций обновления. В EF 6 легко настроить code First для использования хранимых процедур.
В 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
.В консоли управления пакетами введите следующую команду:
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)" ); }
В консоли управления пакетами введите следующую команду:
update-database
Запустите приложение в режиме отладки, перейдите на вкладку "Отделы" и нажмите кнопку "Создать".
Введите данные для нового отдела и нажмите кнопку "Создать".
В Visual Studio просмотрите журналы в окне вывода , чтобы увидеть, что хранимая процедура использовалась для вставки новой строки Отдела.
Код сначала создает имена хранимых процедур по умолчанию. Если вы используете существующую базу данных, может потребоваться настроить имена хранимых процедур, чтобы использовать хранимые процедуры, уже определенные в базе данных. Дополнительные сведения о том, как это сделать, см. в разделе Entity Framework Code First Insert/Update/Delete Stored Procedures.
Если вы хотите настроить созданные хранимые процедуры, можно изменить шаблонный код для метода миграции Up
, создающего хранимую процедуру. Таким образом изменения отражаются при каждом запуске миграции и будут применяться к рабочей базе данных при автоматическом выполнении миграции в рабочей среде после развертывания.
Если вы хотите изменить существующую хранимую процедуру, созданную в предыдущей миграции, можно использовать команду Add-Migration для создания пустой миграции, а затем вручную написать код, вызывающий метод AlterStoredProcedure .
Развернуть в Azure
В этом разделе требуется выполнить необязательное развертывание приложения в Azure в руководстве по миграции и развертыванию этой серии. Если у вас были ошибки миграции, которые были устранены путем удаления базы данных в локальном проекте, пропустите этот раздел.
В Visual Studio щелкните правой кнопкой мыши проект в обозревателе решений и выберите Опубликовать в контекстном меню.
Нажмите кнопку Опубликовать.
Visual Studio развертывает приложение в Azure, а приложение открывается в браузере по умолчанию, работающем в Azure.
Проверьте приложение, чтобы проверить его работу.
При первом запуске страницы, которая обращается к базе данных, Entity Framework выполняет все методы
Up
миграции, необходимые для обновления базы данных с текущей моделью данных. Теперь вы можете использовать все веб-страницы, добавленные с момента последнего развертывания, включая страницы отдела, добавленные в этом руководстве.
Получение кода
Скачивание завершенного проекта
Дополнительные ресурсы
Ссылки на другие ресурсы Entity Framework можно найти в ASP.NET доступ к данным — рекомендуемые ресурсы.
Следующие шаги
Изучив это руководство, вы:
- Сведения об асинхронном коде
- Создание контроллера отдела
- Используемые хранимые процедуры
- Развернуто в Azure
Перейдите к следующей статье, чтобы узнать, как обрабатывать конфликты при одновременном обновлении одной и той же сущности несколькими пользователями.