Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Migrace Code First představuje doporučený způsob, jak vyvíjet schéma databáze aplikace, pokud používáte pracovní postup Code First. Migrace obsahují sadu nástrojů, které umožňují:
- Vytvořit počáteční databázi, která spolupracuje s modelem EF
- Generovat migrace za účelem sledování změn prováděných v modelu EF
- Aktualizovat databázi na základě těchto změn
V následujícím návodu získáte přehled o migracích Code First v Entity Framework. Můžete si buď projít celý návod, nebo přeskočit na téma, které vás zajímá. Probírána jsou následující témata:
Sestavení počátečního modelu a databáze
Než začneme používat migrace, potřebujeme projekt a model Code First, s nímž budeme pracovat. Pro účely tohoto návodu použijeme kanonický model blogu a příspěvku.
- Vytvořte novou konzolovou aplikaci MigrationsDemo
- Přidání nejnovější verze balíčku NuGet EntityFramework do projektu
- Nástroje –> Správce balíčků knihovny –> Konzola Správce balíčků
- Spusťte příkaz Install-Package EntityFramework
- Přidejte soubor Model.cs s kódem zobrazeným níže. Tento kód definuje jednu třídu Blog, která tvoří náš doménový model, a třídu BlogContext, která je naším kontextem EF Code First.
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
}
}
- Teď, když máme model, je čas ho použít k přístupu k datům. Aktualizujte soubor Program.cs níže uvedeným kódem.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
foreach (var blog in db.Blogs)
{
Console.WriteLine(blog.Name);
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Spusťte aplikaci a uvidíte, že se vytvořila databáze MigrationsCodeDemo.BlogContext.
Povolení migrací
Je čas provést s modelem několik dalších změn.
- Pojďme ve třídě Blog použít vlastnost Url.
public string Url { get; set; }
Pokud byste aplikaci spustili znovu, získali byste výjimku InvalidOperationException, která hlásí, že model, který zálohuje kontext BlogContext, se od vytvoření databáze změnil. Zvažte použití Migrace Code First k aktualizaci databáze (http://go.microsoft.com/fwlink/?LinkId=238269).
Jak nám výjimka napovídá, je čas začít používat Code First Migrations. Prvním krokem je povolit migrace pro náš kontext.
Spusťte příkaz Enable-Migrations v konzole Správce balíčků
Tento příkaz přidal do našeho projektu složku Migrations. Ta obsahuje dva soubory:
Třída Konfigurace. Tato třída umožňuje nakonfigurovat chování migrací pro váš kontext. Pro účely tohoto návodu použijeme jen výchozí konfiguraci. Vzhledem k tomu, že projekt obsahuje jen jeden kontext Code First, příkaz Enable-Migrations automaticky vyplní typ kontextu, na který se tato konfigurace vztahuje.
Migrace InitialCreate. Tato migrace byla vygenerována, protože jsme již před povolením migrací nechali Code First vytvořit databázi pro nás. Kód v této vygenerované migraci představuje objekty, které již byly vytvořeny v databázi. V našem případě to je tabulka Blog se sloupci BlogId a Name. Název souboru obsahuje časové razítko, které usnadňuje řazení. Pokud by databáze ještě nebyla vytvořena, nebyla by migrace InitialCreate do projektu přidána. Místo toho by byl při prvním volání Add-Migration jako návrh vygenerován kód pro vytvoření těchto tabulek pro novou migraci.
Více modelů cílících na stejnou databázi
Při použití verzí starších než EF6 bylo možné použít k vygenerování a správě schématu databáze jen jeden model Code First. To bylo proto, že databáze obsahovala jen jednu tabulku __MigrationsHistory bez možnosti zjistit, které položky patří do kterého modelu.
Počínaje verzí EF6 obsahuje třída Configuration vlastnost ContextKey. Ta slouží jako jedinečný identifikátor pro každý model Code First. Odpovídající sloupec v tabulce __MigrationsHistory umožňuje sdílet tabulku položkám z více modelů. Ve výchozím nastavení je tato vlastnost nastavena na plně kvalifikovaný název kontextu.
Generování a spouštění migrací
Migrace Code First má dva hlavní příkazy, s nimiž se obeznámíte.
- Add-Migration vygeneruje další migraci na základě změn, které jste provedli v modelu od vytvoření poslední migrace
- Update-Database použije všechny čekající migrace v databázi.
Potřebujeme vygenerovat migraci, abychom použili nově přidanou vlastnost Url. Příkaz Add-Migration nám umožňuje migrace pojmenovat, takže v našem případě to bude AddBlogUrl.
- V konzole Správce balíčků spusťte příkaz Add-Migration AddBlogUrl.
- Ve složce Migrations teď máme novou migraci AddBlogUrl. Název souboru migrace je předponován časovým razítkem s usnadněním řazení.
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddBlogUrl : DbMigration
{
public override void Up()
{
AddColumn("dbo.Blogs", "Url", c => c.String());
}
public override void Down()
{
DropColumn("dbo.Blogs", "Url");
}
}
}
Teď bychom mohli migraci upravit nebo doplnit, ale všechno vypadá dobře. Pomocí příkazu Update-Database použijeme tuto migraci v databázi.
- V konzole Správce balíčků spusťte příkaz Update-Database.
- Migrace Code First porovná migrace v naší složce Migrations s těmi, které byly aplikovány na databázi. Zjistí, že migraci AddBlogUrl je třeba aplikovat, a spustí ji.
Databáze MigrationsDemo.BlogContext se teď aktualizovala tak, že tabulka Blogs obsahuje sloupec Url.
Přizpůsobení migrací
Zatím jsme vygenerovali a spustili migraci bez provedení jakýchkoli změn. Teď se podíváme na úpravy kódu, který se generuje ve výchozím nastavení.
- Je čas provést v našem modelu několik dalších změn, takže pojďme do třídy Blog přidat vlastnost Rating
public int Rating { get; set; }
- Pojďme také přidat novou třídu Post
public class Post
{
public int PostId { get; set; }
[MaxLength(200)]
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
- Kromě toho přidáme do třídy Blog kolekci Posts, abychom mezi Blog a Post vytvořili druhý konec vztahu.
public virtual List<Post> Posts { get; set; }
Pomocí příkazu Add-Migration necháme Code First Migrations vygenerovat návrh migrace podle nejlepšího odhadu. Tuto migraci nazveme AddPostClass.
- V konzole Správce balíčků spusťte příkaz Add-Migration AddPostClass.
Migrace Code First si při připravování těchto změn vedla poměrně dobře, ale existuje několik věcí, které bychom mohli chtít změnit:
- Nejprve přidáme do sloupce Posts.Title jedinečný index (přidání ve řádku 22 a 29 v níže uvedeném kódu).
- Přidáváme také sloupec Blogs.Rating, který nemůže mít hodnotu null. Pokud jsou v tabulce již nějaká data, přiřadí se jim standardní výchozí hodnota datového typu CLR pro nový sloupec (Rating je celé číslo, takže to bude 0). Chceme ale zadat výchozí hodnotu 3, aby stávající řádky v tabulce Blog začínaly obstojným hodnocením. (V kódu níže vidíte výchozí hodnotu na řádku 24.)
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddPostClass : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Posts",
c => new
{
PostId = c.Int(nullable: false, identity: true),
Title = c.String(maxLength: 200),
Content = c.String(),
BlogId = c.Int(nullable: false),
})
.PrimaryKey(t => t.PostId)
.ForeignKey("dbo.Blogs", t => t.BlogId, cascadeDelete: true)
.Index(t => t.BlogId)
.Index(p => p.Title, unique: true);
AddColumn("dbo.Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
}
public override void Down()
{
DropIndex("dbo.Posts", new[] { "Title" });
DropIndex("dbo.Posts", new[] { "BlogId" });
DropForeignKey("dbo.Posts", "BlogId", "dbo.Blogs");
DropColumn("dbo.Blogs", "Rating");
DropTable("dbo.Posts");
}
}
}
Naše upravená migrace je hotová, takže pojďme pomocí příkazu Update-Database aktualizovat databázi. Tentokrát specifikujeme příznak–Verbose, abyste viděli skript SQL, který migrace Code First spouští.
- V konzole Správce balíčků spusťte příkaz Update-Database –Verbose.
Přenos dat / vlastní SQL
Zatím jsme se věnovali operacím migrace, které nemění ani nepřesunují žádná data. Teď se ale pojďme na pohyb dat podívat blíže. Pro pohyby dat zatím není k dispozici žádná podpora, ale v našem skriptu můžeme kdykoliv spustit určité libovolné příkazy SQL.
- Pojďme do modelu přidat vlastnost Post.Abstract. Později předvyplníme sloupec Abstract u stávajících příspěvků textem ze začátku sloupce Content.
public string Abstract { get; set; }
Pomocí příkazu Add-Migration necháme Code First Migrations na základě svého nejlepšího odhadu vytvořit migraci.
- V konzole Správce balíčků spusťte příkaz Add-Migration AddPostAbstract.
- Vygenerovaná migrace se postará o změny schématu. My ale také chceme předvyplnit sloupec Abstract prvními 100 znaky obsahu každého příspěvku. To uděláme tak, že po přidání sloupce přejdeme na skript SQL a spustíme příkaz UPDATE. (Přidán do řádku 12 v následujícím kódu.)
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddPostAbstract : DbMigration
{
public override void Up()
{
AddColumn("dbo.Posts", "Abstract", c => c.String());
Sql("UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
}
public override void Down()
{
DropColumn("dbo.Posts", "Abstract");
}
}
}
Naše upravená migrace vypadá dobře, takže pojďme pomocí příkazu Update-Database aktualizovat databázi. Specifikujeme příznak –Verbose, abychom viděli, jak se skript SQL spouští v databázi.
- V konzole Správce balíčků spusťte příkaz Update-Database –Verbose.
Migrace na konkrétní verzi (včetně downgradu)
Zatím jsme vždy upgradovali na nejnovější migraci, ale může se stát, že budete chtít upgradovat nebo downgradovat na nějakou konkrétní migraci.
Řekněme, že chceme migrovat databázi do stavu, ve kterém byla po spuštění migrace AddBlogUrl. Můžeme použít přepínač –TargetMigration k downgradu na tuto migraci.
- V konzole Správce balíčků spusťte příkaz Update-Database –TargetMigration: AddBlogUrl.
Tento příkaz spustí skript Down pro migraci AddBlogAbstract a AddPostClass.
Pokud se chcete vrátit až na prázdnou databázi, můžete použít příkaz Update-Database –TargetMigration: $InitialDatabase.
Získání skriptu SQL
Pokud chce mít jiný vývojář tyto změny na svém počítači, stačí,když je jen synchronizuje, jakmile je zavedeme do správy zdrojového kódu. Jakmile budou mít naše nové migrace, stačí spustit příkaz Update-Database, a změny se projeví lokálně. Pokud ale chceme tyto změny odeslat na testovací server a následně do produkčního prostředí, budeme potřebovat skript SQL, který předáme správci databáze.
- Spusťte příkaz Update-Database, ale tentokrát zadejte příznak –Script, aby se změny nepoužily, ale zapsaly do skriptu. Specifikujeme také zdrojovou a cílovou migraci, pro níž se skript vygeneruje. Chceme, aby skript přešel z prázdné databáze ($InitialDatabase) na nejnovější verzi (migraceAddPostAbstract). Pokud cílovou migraci nezadáte, jako cíl se použije nejnovější migrace. Pokud nezadáte zdrojovou migraci, nástroj Migrations použije aktuální stav databáze.
- V konzole Správce balíčků spusťte příkaz Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration: AddPostAbstract.
Migrace Code First provede migrační proces, ale místo skutečné aplikace změn je zapíše do souboru .sql pro vás. Po vygenerování se skript otevře ve Visual Studiu, kde si ho můžete prohlédnout a uložit.
Generování idempotentních skriptů
Počínaje verzí EF6 platí, že pokud zadáte příkaz –SourceMigration $InitialDatabase, bude vygenerovaný skript idempotentní. Idempotentní skripty mohou jakoukoli verzi databáze upgradovat na nejnovější verzi (nebo specifikovanou verzi, pokud použijete –TargetMigration). Vygenerovaný skript obsahuje logiku pro kontrolu tabulky __MigrationsHistory a zavede pouze změny, které nebyly zavedeny dříve.
Automatický upgrade při spuštění aplikace (inicializátor MigrateDatabaseToLatestVersion)
Když nasazujete aplikaci, možná budete chtít, aby při spuštění aplikace automaticky upgradovala databázi (použitím všech čekajících migrací). Registraci inicializátoru databáze MigrateDatabaseToLatestVersion můžete provést takto. Inicializační výraz databáze jednoduše obsahuje logiku, která se používá k zajištění správného nastavení databáze. Tato logika se spustí při prvním použití kontextu v procesu aplikace (AppDomain).
Na příkladu níže aktualizujeme soubor Program.cs tak, aby se nastavil inicializační výraz MigrateDatabaseToLatestVersion pro BlogContext ještě před použitím kontextu (řádek 14). Všimněte si, že také musíme přidat příkaz using pro obor názvů System.Data.Entity (řádek 5).
Když vytvoříme instanci tohoto inicializačního výrazu, musíme zadat typ kontextu (BlogContext) a konfiguraci migrací (Configuration) – konfigurace migrací je třída, která se přidala do složky Migrations, když jsme povolili migrace.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using MigrationsDemo.Migrations;
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Configuration>());
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
foreach (var blog in db.Blogs)
{
Console.WriteLine(blog.Name);
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Při každém spuštění naší aplikace nyní nejprve zkontroluje, zda je cílová databáze aktuální. Pokud není, použije všechny čekající migrace.