我一直在学习如何使用EF一周左右,而且我一直坚持创建/更新数据库的问题。如果数据库不存在,我可以创建一个初始化器来创建数据库:
static class Program
{
static void Main()
{
Database.SetInitializer<GumpDatabase>(new GumpDatabaseInitializer());
....
class GumpDatabaseInitializer : CreateDatabaseIfNotExists<GumpDatabase>
{
public GumpDatabaseInitializer()
{
}
protected override void Seed(GumpDatabase context)
{
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)");
// Other stuff
}
}
或者我可以创建一个配置来迁移db
static class Program
{
static void Main()
{
Database.SetInitializer<GumpDatabase>(new MigrateDatabaseToLatestVersion<GumpDatabase, Configuration>());
....
internal sealed class Configuration : DbMigrationsConfiguration<GumpDatabase>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());
}
protected override void Seed(GumpDatabase context)
{
}
每个都正常工作,但我还没有找到办法做到这两点。我可以通过更改SetInitializer调用在两个初始化程序之间切换,但是如果我想创建数据库,如果它不存在,并且如果它是我该怎么做的话还要迁移它?我是否需要创建自定义初始化程序?
由于
根据NSGaga回答编辑
class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext>
where TContext : DbContext
where TConfiguration : DbMigrationsConfiguration<TContext>, new()
{
private readonly DbMigrationsConfiguration _configuration;
public CreateOrMigrateDatabaseInitializer()
{
_configuration = new TConfiguration();
}
public CreateOrMigrateDatabaseInitializer(string connection)
{
Contract.Requires(!string.IsNullOrEmpty(connection), "connection");
_configuration = new TConfiguration
{
TargetDatabase = new DbConnectionInfo(connection)
};
}
void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context)
{
Contract.Requires(context != null, "context");
if (context.Database.Exists())
{
if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false))
{
var migrator = new DbMigrator(_configuration);
migrator.Update();
}
}
else
{
context.Database.Create();
Seed(context);
context.SaveChanges();
}
}
protected virtual void Seed(TContext context)
{
}
}
和
internal sealed class Configuration : DbMigrationsConfiguration<GumpDatabase>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = false;
SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());
}
protected override void Seed(GumpDatabase context)
{
}
}
和
class GumpDatabaseInitializer : CreateOrMigrateDatabaseInitializer<GumpDatabase,Gump.Migrations.Configuration>
{
public GumpDatabaseInitializer()
{
}
protected override void Seed(GumpDatabase context)
{
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)");
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Sequences (Name)");
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX StationPartNumber ON StationPartNumbers (StationId,PartNumberId)");
}
}
最后
static void Main()
{
Database.SetInitializer<GumpDatabase>(new GumpDatabaseInitializer());
答案 0 :(得分:16)
我认为你几乎就在那里 - 你可以查找MigrateDatabaseToLatestVersion
的源代码(它的开源http://entityframework.codeplex.com/) - 它非常简单,它的作用几乎就是调用{{1 - 据我所见。
所有你需要做的似乎是合并两个 - 使用一个或另一个作为基础,在那里添加其他功能 - 这应该工作正常我认为。
DbMigrator
这样称呼它......
class CreateAndMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext>
where TContext : DbContext
where TConfiguration : DbMigrationsConfiguration<TContext>, new()
{
private readonly DbMigrationsConfiguration _configuration;
public CreateAndMigrateDatabaseInitializer()
{
_configuration = new TConfiguration();
}
public CreateAndMigrateDatabaseInitializer(string connection)
{
Contract.Requires(!string.IsNullOrEmpty(connection), "connection");
_configuration = new TConfiguration
{
TargetDatabase = new DbConnectionInfo(connection)
};
}
void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context)
{
Contract.Requires(context != null, "context");
var migrator = new DbMigrator(_configuration);
migrator.Update();
// move on with the 'CreateDatabaseIfNotExists' for the 'Seed'
base.InitializeDatabase(context);
}
protected override void Seed(TContext context)
{
}
}
...实际上,覆盖它(因为它是通用实现)就像你为Database.SetInitializer(new CreateAndMigrateDatabaseInitializer<GumpDatabase, YourNamespace.Migrations.Configuration>());
所做的那样(你只需要配置额外的'param') - 并且只提供'种子'。
CreateDatabaseIfNotExists
...并称之为
class GumpDatabaseInitializer : CreateAndMigrateDatabaseInitializer<GumpDatabase, YourNamespace.Migrations.Configuration>
{
protected override void Seed(GumpDatabase context)
{
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)");
}
}
修改强> 根据评论 - DbMigrator不应该运行两次。它总是检查(花费一点时间)并进行“空白”更新并继续前进。但是,如果你想在进入之前删除它并“检查” - 这应该有效(改变上面的类似部分)......
Database.SetInitializer(new GumpDatabaseInitializer());
(这是一个冗余/双重检查 - 其中一个if-s应该足够了。在那里休息一下 - 看看究竟发生了什么,它不应该进入 - 一旦Db被迁移。正如我所提到的,有效我测试时很好。
修改强>
将var migrator = new DbMigrator(_configuration);
if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false))
if (migrator.GetPendingMigrations().Any())
migrator.Update();
的内部替换为...
InitializeDatabase
如果首先迁移,这可以解决(中途)非播种问题。迁移必须是第一个,否则你就会遇到问题。
你仍然需要正确地做到这一点 - 如果不是你可能需要的话,这是要点 - 但如果有任何问题与MySQL等,可能还有一些更多的工作在这里。
注意:如果你有一个数据库,仍然没有调用播种,但它是空的。问题是混合两种不同的初始化器。所以你必须解决这个问题 - 通过实现内部的Create ...(我们不能调用的那个)或其他东西。
答案 1 :(得分:1)
实际上它应该是:
var migrator = new DbMigrator(_configuration);
if (!context.Database.CompatibleWithModel(false) || migrator.GetPendingMigrations().Any())
migrator.Update();
因为如果我们有一个与我们的数据库模型无关的迁移,例如在我们的任何表中插入一行,就不会执行迁移。
答案 2 :(得分:1)
要同时执行这两项操作(种子和迁移),您只需使用MigrateDatabaseToLatestVersion
初始化程序进行迁移。为上下文启用迁移时,会创建从Configuration
派生的DbMigrationsConfiguration
类,您可以覆盖Seed
方法来为数据库设定种子。请注意,在执行此方法时,数据库可能已包含种子数据,但AddOrUpdate
扩展方法可以方便地帮助您在数据库中进行“upserts”。
与某些其他数据库初始化器的Seed
方法相比,这是不同的,其中数据库仅在最初创建时播种。但是,当您使用迁移时,您可能希望在数据库更改时更改种子数据,并使用MigrateDatabaseToLatestVersion
使其成为可能。
要将种子与迁移相结合,您必须在新项目中执行以下步骤:
使用关联实体创建代码优先DbContext
在包管理器控制台中,执行命令Enable-Migrations
在Migrations
文件夹中,使用Configuration
方法生成Seed
类。您可以修改此方法以为数据库设定种子:
protected override void Seed(MyContext context) {
// Add two entities with name "Foo" and "Bar".
context.MyEntities.AddOrUpdate(
e => e.Name,
new MyEntity { Name = "Foo" },
new MyEntity { Name = "Bar" }
);
}
您需要创建一个派生自MigrateDatabaseToLatestVersion
的数据库初始化程序:
class MyContextInitializer
: MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration> { }
您还必须通过在应用程序启动时调用Database.SetInitializer(new MyContextInitializer())
或使用<databaseInitializer/>
元素调用App.config
文件来配置初始值设定项。
在生成的Configuration
类的构造函数中,您可以启用自动迁移:
public Configuration() {
AutomaticMigrationsEnabled = true
}
但是,在团队中,您可能不愿意这样做。在这种情况下,您将必须创建初始迁移(除非它是在您Enable-Migrations
时创建的)。在包管理器中执行命令Add-Migration InitialCreate
。这将创建创建数据库所需的第一次迁移。
此时,您有DbContext
迁移和Seed
方法。
总而言之:启用迁移,使用MigrateDatabaseToLatestVersion
初始值设定项,并在启用迁移时生成的Configuration
类中添加种子数据。
答案 3 :(得分:0)
虽然 MigrateDatabaseToLatestVersion 确实创建了数据库(如果它不存在),甚至允许您播种,如果您已经有基于 CreateDatabaseIfNotExists 的工作解决方案和/或不希望通过测试种子数据的存在来使其复杂化,您可以通过继承而不是从 CreateDatabaseIfNotExists 中使用以下内容:
public class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext>
where TContext : DbContext
where TConfiguration : DbMigrationsConfiguration<TContext>, new()
{
void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context)
{
if (context.Database.Exists())
{
if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false))
{
var migrationInitializer = new MigrateDatabaseToLatestVersion<TContext, TConfiguration>(true);
migrationInitializer.InitializeDatabase(context);
}
}
base.InitializeDatabase(context);
}
}
这是基于之前的答案和OP自己的解决方案。这也适用于其他提供商,但我只测试过SQL Server。