Supponiamo ad esempio d'avere il seguente schema di partenza :
E di voler passare al seguente schema di destinazione :
Durante il passaggio dal primo schema al secondo schema, si vuole preservare i dati e quindi eseguire un update durante la migration.
Genererà un codice simile a questo :
public partial class SistemazioneVolo : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.Voli", "IdConnessione", "dbo.Connessiones");
DropIndex("dbo.Voli", new[] { "IdConnessione" });
AddColumn("dbo.Voli", "IdCittaFrom", c => c.Int());
AddColumn("dbo.Voli", "IdCittaTo", c => c.Int());
CreateIndex("dbo.Voli", "IdCittaPartenza");
CreateIndex("dbo.Voli", "IdCittaDestinazione");
AddForeignKey("dbo.Voli", "IdCittaFrom", "dbo.Citta", "ID");
AddForeignKey("dbo.Voli", "IdCittaTo", "dbo.Citta", "ID");
DropColumn("dbo.Voli", "IdConnessione");
}
public override void Down()
{
AddColumn("dbo.Voli", "IdConnessione", c => c.Int());
CreateIndex("dbo.Voli", "IdConnessione");
AddForeignKey("dbo.Voli", "IdConnessione", "dbo.Connessiones", "Id");
DropForeignKey("dbo.Voli", "IdCittaFrom", "dbo.Citta");
DropForeignKey("dbo.Voli", "IdCittaTo", "dbo.Citta");
DropIndex("dbo.Voli", new[] { "IdCittaFrom" });
DropIndex("dbo.Voli", new[] { "IdCittaTo" });
DropColumn("dbo.Voli", "IdCittaDestinazione");
DropColumn("dbo.Voli", "IdCittaPartenza");
}
}
Dovremmo quindi nel metodo Up agguingere lo script di migrazione SQL :
update Voli set [IdCittaFrom] = c.IdCittaFrom, [IdCittaTo] =c.IdCittaTo from Voli v inner join Connessiones c on v.IdConnessione = c.Id
Il metodo Up diventerà quindi :
public override void Up()
{
DropForeignKey("dbo.Voli", "IdConnessione", "dbo.Connessiones");
DropIndex("dbo.Voli", new[] { "IdConnessione" });
AddColumn("dbo.Voli", "IdCittaFrom", c => c.Int());
AddColumn("dbo.Voli", "IdCittaTo", c => c.Int());
CreateIndex("dbo.Voli", "IdCittaPartenza");
CreateIndex("dbo.Voli", "IdCittaDestinazione");
AddForeignKey("dbo.Voli", "IdCittaFrom", "dbo.Citta", "ID");
AddForeignKey("dbo.Voli", "IdCittaTo", "dbo.Citta", "ID");
Sql("update Voli set [IdCittaFrom] = c.IdCittaFrom, [IdCittaTo] =c.IdCittaTo from Voli v inner join Connessiones c on v.IdConnessione = c.Id");
DropColumn("dbo.Voli", "IdConnessione");
}
Se vogliamo dare la compatibilità anche nel "tornare indietro" alla migrazione precedente dovremo provedere alla creazione dello script sql nel metodo Down.Oltre a degli update possiamo fare degli insert, ad esempio nel caso di creazione di dizionari che contengano dei valori predefiniti.
Se si hanno però molte insert da fare, forse è il caso di spostare questi ultimi in uno script sql da eseguire durante la migrazione.
Ad esempio :
public partial class AggiuntoCapAiComuni : ExpandedDbMigration
{
public override void Up()
{
...
SqlFile(@"Bin\Migrations\Sql\InserimentoComuni.sql");
}
public override void Down()
{
...
}
}
Come potrete notare, in questo caso abbiamo cambiato la classe base, non più DbMigration bensì ExpandedDbMigration.
Questo il codice :
public abstract class ExpandedDbMigration : System.Data.Entity.Migrations.DbMigration
{
///
/// Drops an index of the name specified, on the table specified.
///
/// The built-in DropIndex command in Migrations interprets your command in a variety of ways that can translate into
/// multiple SQL statements, or dropping an index with an expanded name based on Conventions. This command cuts past those
/// interpretations and drops exactly what you requested.
///
/// Useful on existing databases where indices pre-existed, or where indices have been created by an outside tool like
/// Sql Server Missing Indexes.
///
public void DropIndexExact(string table, string indexName)
{
Sql("drop index [" + indexName + "] on [" + table + "]");
}
public void DropConstraint(string table, string constraintName)
{
Sql("alter table [" + table + "] drop constraint [" + constraintName + "]");
}
///
/// Drop a Primary Key by name. See DropIndexExact for an explanation of how Exact calls differ from EF Migrations built-ins.
///
public void DropPrimaryKeyExact(string table, string pkName)
{
DropConstraint(table, pkName);
}
///
/// Drop a Foreign Key by name. See DropIndexExact for an explanation of how Exact calls differ from EF Migrations built-ins.
///
public void DropForeignKeyExact(string table, string fkName)
{
DropConstraint(table, fkName);
}
///
/// Reads in a .sql file filled with SQL statements and emits them as part of a Migration's data changes ("data motion").
///
/// Usage: SqlFile(@"Migrations\Sql\2013-08-15 FillTable.sql");
///
/// Note: All statements will presumably operate on specific table names, which will need to exist in this database
/// already to work properly.
///
/// Note: Any error will cause the entire Migration to fail, as EF Migrations always do.
///
/// Note: Many statements common in SQL scripts are illegal in Migrations commands, especially "GO".
/// To compensate for this, this call tries to filter out common problem statements - but it might be wise to clean out
/// your sql script beforehand. Or, just run it and see what happens - the migration always runs in a Transaction so if it
/// fails no harm done.
///
/// The path to the .sql script relative to the Project that contains this Migration.
public void SqlFile(string path)
{
var cleanAppDir = new Regex(@"\\bin.+");
var dir = AppDomain.CurrentDomain.BaseDirectory;
dir = cleanAppDir.Replace(dir, "") + @"\";
var sql = File.ReadAllLines(dir + path);
string[] ignore = new string[]
{
"GO", // Migrations doesn't support GO
"/*", // Migrations might not support comments
"print" // Migrations might not support print
};
foreach (var line in sql)
{
if (ignore.Any(ig => line.StartsWith(ig)))
continue;
if (!string.IsNullOrEmpty(line.Trim()))
{
Sql(line);
}
}
}
}

