EF中IDatabaseInitializer的正确使用是什么? [英] What is the correct use of IDatabaseInitializer in EF?
问题描述
我有一个自定义的DatabaseInitialiser,位于
///< summary>
///实现IDatabaseInitializer,为上下文提供自定义数据库初始化。
///< / summary>
///< typeparam name =TContext> TContext是DbContext< / typeparam>
public class ParikshaDataBaseInitializer< TContext> :IDatabaseInitializer< TContext>其中TContext:DbContext
{
///< summary>
///初始化数据库的方法。
///注意数据库无法删除,因为它在删除和重新创建数据库时正在使用中。
///< / summary>
///< param name =context>运行初始化器< / param>的DbContext
public void InitializeDatabase(TContext context)
{
var exists = context.Database.Exists();
try
{
if(exists&&&&context.Database.CompatibleWithModel(true))
{
//一切都很好,我们完成
返回;
}
if(!exists)
{
context.Database.Create();
}
}
catch(异常)
{
//有些错误,我们找不到元数据或模型不兼容。
if(exists)
{
context.Database.ExecuteSqlCommand(ALTER DATABASE Pariksha SET SINGLE_USER WITH ROLLBACK IMMEDIATE);
context.Database.ExecuteSqlCommand(USE Master DROP DATABASE Pariksha);
context.SaveChanges();
}
context.Database.Create();
}
}
}
有关上述代码的内容不仅仅是hacky(随意帮助)
然后,我添加了迁移,并使迁移脚本也正常工作。
内部密封类配置:DbMigrationsConfiguration& ParikshaContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey =EFRepository.Context.ParikshaContext;
}
protected override void Seed(ParikshaContext context)
{
}
}
迁移按预期工作。
现在,我的应用程序启动中有什么问题应该怎么办?
这样的东西
var config = new Configuration();
var migrator = new DbMigrator(config);
migrator.Update();
一些论坛在构造函数中也提出了这样的建议,似乎有点奇怪,因为我不想以检查每次使用上下文时db和schema是否正确。那么可能使用这种技术或者是否将建议的上下文看作是错误的?
public ParikshaContext ):base(Pariksha)
{
Database.SetInitializer(new ParikshaDataBaseInitializer& ParikshaContext>());
}
总而言之,
-
可用的不同技术的正确用例是什么?
-
什么是理想策略迁移在所有情况下工作,何时将数据库从一个环境移动到另一个环境?
这是我尝试 Db Initializer
,它结合了 Migration
初始化程序和默认 Db创建
一个播种。 (注意:这不是理想的,更像是一个简单的练习,但是给出了一个解决方案,这里的问题主要是工作 - 只需检查我所做的所有更新)。
对于为什么
和如何
- 完全明白我建议您也参考 EF源代码(这是新版本,但在许多方面相似)
< h3> 1)
a)Db初始化器通常只调用一次(每个连接) - 当您尝试访问模型第一次(第一次查询或类似)。在您的初始化程序中放置一个断点来检查。
所以把它放在构造函数中是完全安全的(虽然我喜欢它在启动时也是配置)。 只有在需要初始化(并且最后一个
被使用)时才会调用它,您不应手动调用它。
无论如何,强制初始化程序可以执行 this.Database.Initialize(force:true);
/ p>
当切换连接时看到我的问题发布
不使用IDbContextFactory的代码第一个自定义连接字符串和迁移
b)如果您创建自己的 IDatabaseInitializer
,而您仍然希望将迁移工作并行
你不应该只是调用 DbMigrator
- 由于您的自定义初始化程序将错过整个数据库创建(例如,如果你想种子或东西 - 检查我的示例上面)。 从外部
这两件事情都是有效的初始化器 - 所以你需要将它们整合到一个中,这将链条
某些东西。请记住,执行顺序
是重要的(请参见上面的问题) - 您应该检查'空条件',然后调用 DbMigrator
,然后做自己的初始化。我使用一个初始化器作为基类,并合并另一个。
如果您只想要 seed
- 您可以使用迁移配置,这是最简单的,如果合理。
2)
很开放,没有一个答案。通常它有效,但问题出现...
-
迁移是3件事(正如我所见) - 你的代码模型/实体,数据库/表以及Db中的
__ MigrationHistory
系统表。所有3需要保持同步
。如果您不同步,则可以删除迁移表,重新创建迁移(使用一个标志来保留现有的数据库),然后按照以前的步骤进行操作 - 即使用实时数据来解决问题。为此,请参阅如何忽略EF 4.3迁移中的表/类, -
您需要在移动数据库时删除/创建Db的权限,
-
确保您的连接正确(更改配置 - 并与DbContext名称或ctor同步),
-
保持简单,不要做奇怪的事情或切换代码连接(可能但有问题)等。
-
不要
mix database / code
版本 - 即一个代码实体版本 - 一个数据库。如果您想使用不同的代码版本(例如分段,生产)共享相同的Db - 不要(多租户解决方案将在EF6中可用 - 例如 this ), -
如果需要手动应用数据库 - 通过
更新数据库
生成脚本
并应用那不要手动做事情,否则会出错(迁移历史记录表) - 请参阅这一个,
...这仅仅是为了少数几个。这是非常稳定和可用的IMO - 但如果您遵守规则 - 并且知道什么是限制。
类CreateAndMigrateDatabaseInitializer< TContext,TConfiguration>
pre>
:CreateDatabaseIfNotExists< TContext>,IDatabaseInitializer< TContext>
其中TContext:DbContext
其中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)
{
var doseed =!context.Database.Exists();
//&&&新的DatabaseTableChecker()。AnyModelTableExists(context);
//检查是否种子 - 我们缺少'AnyModelTableExists'
// ...可以复制/完成,否则需要...
var migrator = new DbMigrator(_configuration);
// if(doseed ||!context.Database.CompatibleWithModel(false))
if(migrator.GetPendingMigrations()。Any())
migrator.Update();
//继续使用'Seed'的$ CreateDatabaseIfNotExists'
base.InitializeDatabase(context);
if(doseed)
{
种子(上下文);
context.SaveChanges();
}
}
protected override void Seed(TContext context)
{
}
}
I have a custom DatabaseInitialiser which is below
/// <summary> /// Implements the IDatabaseInitializer to provide a custom database initialisation for the context. /// </summary> /// <typeparam name="TContext">TContext is the DbContext</typeparam> public class ParikshaDataBaseInitializer<TContext> : IDatabaseInitializer<TContext> where TContext : DbContext { /// <summary> /// The method to Initialise the database. /// Takes care of the database cannot be dropped since it is in use problem while dropping and recreating the database. /// </summary> /// <param name="context">The DbContext on which to run the initialiser</param> public void InitializeDatabase(TContext context) { var exists = context.Database.Exists(); try { if (exists && context.Database.CompatibleWithModel(true)) { // everything is good , we are done return; } if (!exists) { context.Database.Create(); } } catch (Exception) { //Something is wrong , either we could not locate the metadata or the model is not compatible. if (exists) { context.Database.ExecuteSqlCommand("ALTER DATABASE Pariksha SET SINGLE_USER WITH ROLLBACK IMMEDIATE"); context.Database.ExecuteSqlCommand("USE Master DROP DATABASE Pariksha"); context.SaveChanges(); } context.Database.Create(); } } }
something about the above code is more than just hacky (Feel free to chip in with help)
I then added migrations and got the migration script to work correctly as well.
internal sealed class Configuration : DbMigrationsConfiguration<ParikshaContext> { public Configuration() { AutomaticMigrationsEnabled = false; ContextKey = "EFRepository.Context.ParikshaContext"; } protected override void Seed(ParikshaContext context) { } }
the migrations work as expected.
Now, the questions is in my application startup what should I do ? Something like this
var config = new Configuration(); var migrator = new DbMigrator(config); migrator.Update();
and some forums suggested this as well in the constructor which seems a little odd because I don't want to check if the db and schema are correct everytime I use the Context. So, what could be the possible use of this technique or did I get the context of the suggestion as wrong ?
public ParikshaContext() : base("Pariksha") { Database.SetInitializer(new ParikshaDataBaseInitializer<ParikshaContext>()); }
To summarise,
what is the correct use-case for the different techniques available ?
what would be the ideal strategy so that the migrations work in all conditions and when we move databases from one environment to another ?
解决方案This is my attempt at
Db Initializer
which combines bothMigration
initializer and the defaultDb Create
one with seeding. (note: it's not ideal, more like a simple exercise but gives a solution to what you're asking here, mostly works - just check all the updates I made).How to create initializer to create and migrate mysql database?
As for the
why
andhow
- to fully understand that I'd suggest you also consult the EF source code (that's new version but in many ways similar)1)
a) Db initializer gets called usually only once (per connection) - and when you try to access your 'model' for the first time (first query or similar). Put a breakpoint in your initializer to check.
So it's completely safe to put it within constructor (though I prefer it on startup somewhere, config also). It only gets called when it's needed to initialize (and the
last one set
is used), you shouldn't invoke it manually.Anyhow, to force the initializer you can do the
this.Database.Initialize(force: true);
For when switching connections see my post on problems
Code first custom connection string and migrations without using IDbContextFactoryb) If you create your own
IDatabaseInitializer
and you still want migrations to workside by side
You shouldn't just call
DbMigrator
from outside - as your custom initializer will miss on the whole 'db creation' (e.g. if you wish to seed or something - check my example above).Both things are effectively 'initializers' - so you'd need to integrate them into one, that'd
chain
things in some way. Have in mind thatorder of execution
is important (see above ex. for problems) - you should check for 'empty condition', then callDbMigrator
, then do your own intialization. I used one initializer as a base class, and merged the other.And if you just want to
seed
- you can use the migration Configuration, that's the simplest if plausible.2)
Is pretty 'open ended' and there is no single answer. Usually it works, but issues are expexted...
Migrations are 3 things (as I see it) - your code model/entities, your database/tables, and the
__MigrationHistory
system table in the Db. All 3 need to stayin sync
. If you get 'out of sync', you can drop the migration table, recreate migrations (with a flag to keep existing db) and then move on as before - i.e. there are solutions for when with live data. For this see How to ignore a table/class in EF 4.3 migrations,you'd need permissions to drop/create Db for when moving database,
make sure your connection is right (change in config - and in sync with your DbContext name or ctor),
keep it simple, don't do fancy things or switch connections from code (possible but has problems) etc.,
don't
mix database / code
versions - i.e. one code entities version - one database. If you want to share the same Db with different code versions (e.g. staging, production) - don't (multi-tenant solutions will be available in EF6 - e.g. this),if you need to manually apply the database - generate the
script
viaUpdate-Database
- and apply that, don't do things manually or you'll get it wrong (the migration history table) - see this one,
...that's just to name the few. It is pretty stable and usable IMO - but if you follow the rules - and know what the limitations are.
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)
{
var doseed = !context.Database.Exists();
// && new DatabaseTableChecker().AnyModelTableExists(context);
// check to see if to seed - we 'lack' the 'AnyModelTableExists'
// ...could be copied/done otherwise if needed...
var migrator = new DbMigrator(_configuration);
// if (doseed || !context.Database.CompatibleWithModel(false))
if (migrator.GetPendingMigrations().Any())
migrator.Update();
// move on with the 'CreateDatabaseIfNotExists' for the 'Seed'
base.InitializeDatabase(context);
if (doseed)
{
Seed(context);
context.SaveChanges();
}
}
protected override void Seed(TContext context)
{
}
}
这篇关于EF中IDatabaseInitializer的正确使用是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!