Esercitazione: Usare stored procedure asincrone e con Entity Framework in un'app MVC ASP.NET
Nelle esercitazioni precedenti si è appreso come leggere e aggiornare i dati usando il modello di programmazione sincrono. In questa esercitazione viene illustrato come implementare il modello di programmazione asincrona. Il codice asincrono può aiutare un'applicazione a ottenere prestazioni migliori perché consente un uso migliore delle risorse del server.
In questa esercitazione viene illustrato anche come usare stored procedure per operazioni di inserimento, aggiornamento ed eliminazione in un'entità.
Infine, ridistribuire l'applicazione in Azure, insieme a tutte le modifiche apportate al database implementate dopo la prima distribuzione.
Le figure seguenti illustrano alcune delle pagine che verranno usate.
In questa esercitazione:
- Informazioni sul codice asincrono
- Creare un controller di reparto
- Usare le stored procedure
- Distribuisci in Azure
Prerequisiti
Perché usare il codice asincrono
Per un server Web è disponibile un numero limitato di thread e in situazioni di carico elevato tutti i thread disponibili potrebbero essere in uso. In queste circostanze il server non può elaborare nuove richieste finché i thread non saranno liberi. Con il codice sincrono, può succedere che molti thread siano vincolati nonostante in quel momento non stiano eseguendo alcuna operazione. Rimangono tuttavia in attesa che l'operazione I/O sia completata. Con il codice asincrono, se un processo è in attesa del completamento dell'operazione I/O, il thread viene liberato e il server lo può usare per l'elaborazione di altre richieste. Di conseguenza, il codice asincrono consente alle risorse del server di essere usate in modo più efficiente e il server è abilitato per gestire più traffico senza ritardi.
Nelle versioni precedenti di .NET, la scrittura e il test di codice asincrono erano complessi, soggetti a errori e difficili da eseguire per il debug. In .NET 4.5 scrivere, testare e eseguire il debug di codice asincrono è molto più semplice che in genere è consigliabile scrivere codice asincrono, a meno che non si abbia un motivo per non farlo. Il codice asincrono introduce una piccola quantità di sovraccarico, ma per le situazioni di traffico basso il successo delle prestazioni è trascurabile, mentre per situazioni di traffico elevato, il potenziale miglioramento delle prestazioni è sostanziale.
Per altre informazioni sulla programmazione asincrona, vedere Usare il supporto asincrono di .NET 4.5 per evitare di bloccare le chiamate.
Creare il controller di reparto
Creare un controller di reparto nello stesso modo in cui sono stati caricati i controller precedenti, ad eccezione di questa volta, selezionare la casella di controllo Usa azioni del controller asincrono.
Le evidenziazioni seguenti mostrano cosa è stato aggiunto al codice sincrono per il Index
metodo per renderlo asincrono:
public async Task<ActionResult> Index()
{
var departments = db.Departments.Include(d => d.Administrator);
return View(await departments.ToListAsync());
}
Sono state applicate quattro modifiche per consentire l'esecuzione asincrona della query del database Entity Framework:
- Il metodo è contrassegnato con la
async
parola chiave , che indica al compilatore di generare callback per parti del corpo del metodo e di creare automaticamente l'oggettoTask<ActionResult>
restituito. - Il tipo restituito è stato modificato da
ActionResult
aTask<ActionResult>
. IlTask<T>
tipo rappresenta il lavoro in corso con un risultato di tipoT
. - La
await
parola chiave è stata applicata alla chiamata al servizio Web. Quando il compilatore vede questa parola chiave, dietro le quinte suddivide il metodo in due parti. La prima parte termina con l'operazione avviata in modo asincrono. La seconda parte viene inserita in un metodo di callback chiamato al termine dell'operazione. - È stata chiamata la versione asincrona del
ToList
metodo di estensione.
Perché l'istruzione departments.ToList
viene modificata ma non l'istruzione departments = db.Departments
? Il motivo è che solo le istruzioni che causano l'invio asincrono di query o comandi al database. L'istruzione departments = db.Departments
configura una query, ma la query non viene eseguita finché non viene chiamato il ToList
metodo . Pertanto, solo il ToList
metodo viene eseguito in modo asincrono.
Nel metodo e nei Details
HttpGet
Edit
metodi e Delete
, il Find
metodo è quello che fa sì che una query venga inviata al database, in modo che sia il metodo che viene eseguito in modo asincrono:
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);
}
Nei metodi , HttpPost Edit
e DeleteConfirmed
è la chiamata al SaveChanges
metodo che determina l'esecuzione Create
di un comando, non le istruzioni, ad esempiodb.Departments.Add(department)
, che causano solo la modifica delle entità in memoria.
public async Task<ActionResult> Create(Department department)
{
if (ModelState.IsValid)
{
db.Departments.Add(department);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
Aprire Views\Department\Index.cshtml e sostituire il codice del modello con il codice seguente:
@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>
Questo codice modifica il titolo da Indice a Reparti, sposta il nome amministratore a destra e fornisce il nome completo dell'amministratore.
Nelle visualizzazioni Crea, Elimina, Dettagli e Modifica modificare la didascalia per il InstructorID
campo in "Amministratore" nello stesso modo in cui il campo nome del reparto è stato modificato in "Reparto" nelle visualizzazioni Corso.
Nelle visualizzazioni Crea e Modifica usare il codice seguente:
<label class="control-label col-md-2" for="InstructorID">Administrator</label>
Nelle visualizzazioni Elimina e Dettagli usare il codice seguente:
<dt>
Administrator
</dt>
Eseguire l'applicazione e fare clic sulla scheda Reparti .
Tutto funziona come negli altri controller, ma in questo controller tutte le query SQL vengono eseguite in modo asincrono.
Alcuni aspetti da tenere presenti quando si usa la programmazione asincrona con Entity Framework:
- Il codice asincrono non è thread-safe. In altre parole, non provare a eseguire più operazioni in parallelo usando la stessa istanza di contesto.
- Se si vogliono sfruttare i vantaggi del codice asincrono in termini di prestazioni, verificare che i pacchetti della libreria impiegati, ad esempio per il paging, usino la modalità asincrona per chiamare i metodi di Entity Framework che generano query da inviare al database.
Usare le stored procedure
Alcuni sviluppatori e amministratori di database preferiscono usare stored procedure per l'accesso al database. Nelle versioni precedenti di Entity Framework è possibile recuperare dati usando una stored procedure eseguendo una query SQL non elaborata, ma non è possibile indicare a EF di usare stored procedure per le operazioni di aggiornamento. In EF 6 è facile configurare Code First per l'uso di stored procedure.
In DAL\SchoolContext.cs aggiungere il codice evidenziato al
OnModelCreating
metodo .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(); }
Questo codice indica a Entity Framework di usare stored procedure per operazioni di inserimento, aggiornamento ed eliminazione nell'entità
Department
.In Package Manage Console (Gestione pacchetti) immettere il comando seguente:
add-migration DepartmentSP
Aprire Migrations\<timestamp>_DepartmentSP.cs per visualizzare il codice nel
Up
metodo che crea stored procedure Insert, Update ed 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)" ); }
In Package Manage Console (Gestione pacchetti) immettere il comando seguente:
update-database
Eseguire l'applicazione in modalità di debug, fare clic sulla scheda Reparti e quindi su Crea nuovo.
Immettere i dati per un nuovo reparto e quindi fare clic su Crea.
In Visual Studio esaminare i log nella finestra Output per vedere che è stata usata una stored procedure per inserire la nuova riga Reparto.
Code First crea nomi di stored procedure predefiniti. Se si usa un database esistente, potrebbe essere necessario personalizzare i nomi delle stored procedure per usare stored procedure già definite nel database. Per informazioni su come eseguire questa operazione, vedere Code First insert/Update/Delete stored procedure di Entity Framework Code First.
Se si desidera personalizzare le operazioni eseguite dalle stored procedure generate, è possibile modificare il codice di scaffolding per il metodo delle migrazioni Up
che crea la stored procedure. In questo modo le modifiche vengono riflesse ogni volta che viene eseguita la migrazione e verranno applicate al database di produzione quando le migrazioni vengono eseguite automaticamente nell'ambiente di produzione dopo la distribuzione.
Se si desidera modificare una stored procedure esistente creata in una migrazione precedente, è possibile usare il comando Add-Migration per generare una migrazione vuota e quindi scrivere manualmente codice che chiama il metodo AlterStoredProcedure .
Distribuisci in Azure
Questa sezione richiede di aver completato la sezione facoltativa Distribuzione dell'app in Azure nell'esercitazione Migrazioni e distribuzione di questa serie. Se si sono verificati errori di migrazione risolti eliminando il database nel progetto locale, ignorare questa sezione.
In Visual Studio fare clic con il pulsante destro del mouse sul progetto in Esplora soluzioni e scegliere Pubblica dal menu di scelta rapida.
Fare clic su Pubblica.
Visual Studio distribuisce l'applicazione in Azure e l'applicazione viene aperta nel browser predefinito, in esecuzione in Azure.
Testare l'applicazione per verificare che funzioni.
La prima volta che si esegue una pagina che accede al database, Entity Framework esegue tutti i metodi di migrazione
Up
necessari per aggiornare il database con il modello di dati corrente. È ora possibile usare tutte le pagine Web aggiunte dall'ultima distribuzione, incluse le pagine reparto aggiunte in questa esercitazione.
Ottenere il codice
Scaricare il progetto completato
Risorse aggiuntive
I collegamenti ad altre risorse di Entity Framework sono disponibili in ASP.NET Accesso ai dati - Risorse consigliate.
Passaggi successivi
In questa esercitazione:
- Informazioni sul codice asincrono
- Creazione di un controller di reparto
- Stored procedure usate
- Distribuito in Azure
Passare all'articolo successivo per informazioni su come gestire i conflitti quando più utenti aggiornano contemporaneamente la stessa entità.