Utilizzando EntityFramework in modalità code-first per interagire con il database SQL e mappare su di esso le entità del proprio data model ci si ritrova necessariamente a creare una migration ogni volta che si vuole effettuare modifica e ad applicarla tramite un comando di update.
In questo caso la necessità era quella di modificare il DbContext aggiungendo un indice composito su 3 colonne di una tabella contenente già una discreta quantità di record (qualche milione).
Nell’override del metodo OnModelCreating sono state quindi aggiunte le seguenti dichiarazioni:
modelBuilder.Entity() .Property(f => f.ColumnOne) .HasColumnAnnotation( IndexAnnotation.AnnotationName, new IndexAnnotation(new[] { new IndexAttribute("ThreeColumnIndex") { Order = 1 } } ) ); modelBuilder.Entity () .Property(f => f.ColumnTwo) .HasColumnAnnotation( IndexAnnotation.AnnotationName, new IndexAnnotation(new[] { new IndexAttribute("ThreeColumnIndex") { Order = 2 } } ) ); modelBuilder.Entity () .Property(f => f.ColumnThree) .HasColumnAnnotation( IndexAnnotation.AnnotationName, new IndexAnnotation(new[] { new IndexAttribute("ThreeColumnIndex") { Order = 3 } } ) );
Una volta effettuata la modifica ho lanciato il comando “add-migration CreateIndexMyThreeColumns” che ha creato il relativo file nella cartella “Migrations”, contenente le istruzioni per l’applicazione e la rimozione della migrazione stessa:
namespace MyDataDataModel.Migrations { using System; using System.Data.Entity.Migrations; public partial class CreateIndexMyThreeColumns : DbMigration { public override void Up() { CreateIndex("dbo.MyEntity", new[] { "ColumnOne", "ColumnTwo", "ColumnThree" }, name: "ThreeColumnIndex"); } public override void Down() { DropIndex("dbo.MyEntity", "ThreeColumnIndex"); } } }
A questo punto ho eseguito il comando update-database per l’applicazione della migration al contesto ma, come si può vedere dagli screenshot sottostanti, ho ottenuto un errore di timeout:
L’errore è piuttosto parlante:
System.Data.SqlCliente.SqlException: Execution Timeout Expired. The timeout period elapsed prior to completation of the operation or the server is not reposnding.
La creazione dell’indice sulla tabella già popolata con parecchi record è infatti un’operazione lunga, che non riusciva a completare prima dello scadere del timeout per l’esecuzione dei comandi che di default è 30 secondi.
A questo punto le soluzioni possibili sono 2:
- Aumentare il CommandTimeout del DbContext
- Utilizzare l’opzione -script del comando update-database
In questo articolo vediamo come utilizzare il secondo metodo.
Aggiungendo l’opzione -script al comando update-database quello che succede è che EntityFramework, invece di provare ad applicare la migrazione, genera uno script SQL che possiamo poi lanciare da SQL Server Management Studio.
Proviamo quindi ad eseguirlo:
update-database -script
Come possiamo vedere dall’immagine seguente, dopo aver eseguito il comando, in Visual Studio si apre un nuovo tab contenente lo statement SQL da eseguire:
Lo statement, in questo caso, è il seguente:
CREATE INDEX [ThreeColumnIndex] ON [dbo].[MyEntity]([ColumnOne], [ColumnTwo], [ColumnThree]) INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion]) VALUES (N'201703291516276_CreateIndexMyThreeColumns', N'MyDataDataModel.Migrations.Configuration', 0x1F8B0....., N'6.1.3-40302')
Copiando e incollando lo script in SQL Server Management Studio e poi eseguendolo, l’indice è stato creato senza problemi, evitando qualsiasi problema di timeout.