Ho dovuto affrontare un problema, in cui non riesco a raggiungere lo script SQL per applicare la migrazione. Ecco il mio codice di migrazione:
public partial class AddSomethingMigration : Migration
{
private const string MIGRATION_SQL_SCRIPT_FILE_NAME = @"Migrations\Scripts\20170710123314_AddSomethingMigration.sql";
protected override void Up(MigrationBuilder migrationBuilder)
{
string sql = Path.Combine(Directory.GetParent(Directory.GetCurrentDirectory()).FullName, MIGRATION_SQL_SCRIPT_FILE_NAME));
migrationBuilder.Sql(File.ReadAllText(sql));
}
}
Quindi quando uso la console di Gestione pacchetti sul computer locale tutto funziona correttamente. Ma quando eseguo la distribuzione nell'ambiente ottengo la discrepanza nel file.
Posso eseguire automaticamente i miei script SQL statici tramite la migrazione EF, oppure dovrei incollare la query SQL in linea nel codice?
Ho trovato le varie risposte per questa domanda.
Aggiungi script come risorse del progetto e usalo come:
string sql = Resources._20170630085940_AddMigration;
migrationBuilder.Sql(sql);
Questa opzione non è così buona, perché il .sql si incorpora nell'assembly.
Se si utilizzano progetti Net Core con la struttura .csproj, è possibile aggiungere il gruppo articoli a xml:
<ItemGroup> <Content Include="Migrations\**\*.sql" CopyToPublishDirectory="PreserveNewest" /><!-- CopyToPublishDirectory = { Always, PreserveNewest, Never } --></ItemGroup>
E quindi specificare il percorso per file come:
Path.Combine(AppContext.BaseDirectory, relativePath)
Quello che mi piace fare è incorporare lo script SQL come risorsa nell'assembly in modo che l'assembly non dipenda da alcun file esterno. Ho testato questo approccio con Visual Studio Community 2019 16.4.2. Nel mio caso, DbContext
è contenuto nella libreria .NET Standard 2.0 e la mia applicazione Web esegue .NET Core 2.2.
Per prima cosa devi creare un file di migrazione:
Aggiungi una nuova migrazione:
Add-Migration RunSqlScript
Aggiungi uno script SQL nella cartella di migrazione (lo chiamo con lo stesso prefisso del file di migrazione per comodità)
Nella finestra Proprietà file assicurati che l'azione di compilazione sia "Risorsa incorporata". Nota che non è necessario copiarlo nella cartella di output poiché lo script sql verrà incorporato nell'assembly.
Aggiorna il metodo Su nella migrazione RunSqlScript
var assembly = Assembly.GetExecutingAssembly();
string resourceName = typeof(RunSqlScript).Namespace + ".20191220105024_RunSqlScript.sql";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
using (StreamReader reader = new StreamReader(stream))
{
string sqlResult = reader.ReadToEnd();
migrationBuilder.Sql(sqlResult);
}
}
Nella mia app ho ricodificato questo codice in un metodo di utilità. Ho pubblicato senza questa rifattorizzazione per brevità.
AGGIORNARE:
Il codice ricattorizzato che ho menzionato sopra:
public static class MigrationUtility
{
/// <summary>
/// Read a SQL script that is embedded into a resource.
/// </summary>
/// <param name="migrationType">The migration type the SQL file script is attached to.</param>
/// <param name="sqlFileName">The embeded SQL file name.</param>
/// <returns>The content of the SQL file.</returns>
public static string ReadSql(Type migrationType, string sqlFileName)
{
var assembly = migrationType.Assembly;
string resourceName = $"{migrationType.Namespace}.{sqlFileName}";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
if (stream == null)
{
throw new FileNotFoundException("Unable to find the SQL file from an embedded resource", resourceName);
}
using (var reader = new StreamReader(stream))
{
string content = reader.ReadToEnd();
return content;
}
}
}
}
Esempio di utilizzo:
string sql = MigrationUtility.ReadSql(typeof(RunSqlScript), "20191220105024_RunSqlScript.sql");
migrationBuilder.Sql(sql);