EntityFramework code-first: usare l’opzione -script per evitare il timeout del comando update-database

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:
Update Database command timeoutEntity Framework Update Database timeoutL’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

Entity Framework Update Database -script option

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:
Update Database -script option SQL statement 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.

Leave a Reply

Your email address will not be published. Required fields are marked *