EF 中 IDatabaseInitializer 的正确用法是什么? [英] What is the correct use of IDatabaseInitializer in EF?

查看:15
本文介绍了EF 中 IDatabaseInitializer 的正确用法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个自定义的 DatabaseInitialiser,它在下面

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();
        }
    } 
}

关于上述代码的一些东西不仅仅是hacky(请随时提供帮助)

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)
        {
        }
    }

迁移按预期工作.

现在,问题是在我的应用程序启动中我应该做什么?像这样的

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>());
        }

总结一下,

  1. 不同可用技术的正确用例是什么?

  1. 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 ?

推荐答案

这是我对 Db Initializer 的尝试,它结合了 Migration 初始化程序和默认的 Db使用播种创建一个.(注意:这并不理想,更像是一个简单的练习,但可以解决您在此处提出的问题,大多数情况下都有效 - 只需检查我所做的所有更新).

This is my attempt at Db Initializer which combines both Migration initializer and the default Db 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).

如何创建初始化程序创建和迁移mysql数据库?

至于 whyhow - 要完全理解,我建议您也参考 EF 源代码(这是新版本,但在许多方面相似)

As for the why and how - to fully understand that I'd suggest you also consult the EF source code (that's new version but in many ways similar)

a) Db 初始化程序通常只调用一次(每个连接) - 当您第一次尝试访问模型"时(第一次查询或类似查询).在初始化程序中放置一个断点以进行检查.

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.

无论如何,要强制初始化,您可以执行 this.Database.Initialize(force: true);

Anyhow, to force the initializer you can do the this.Database.Initialize(force: true);

关于切换连接时,请参阅我关于问题的帖子
代码优先自定义连接字符串和迁移使用 IDbContextFactory

For when switching connections see my post on problems
Code first custom connection string and migrations without using IDbContextFactory

b) 如果您创建自己的 IDatabaseInitializer 并且仍希望迁移能够并排运行

b) If you create your own IDatabaseInitializer and you still want migrations to work side by side

您不应该只从外部调用 DbMigrator - 因为您的自定义初始化程序将错过整个数据库创建"(例如,如果您希望播种或其他内容 -检查我上面的例子).

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).

这两件事实际上都是初始化器"——因此您需要将它们集成到一个中,这会以某种方式.请记住 执行顺序 很重要(请参阅上面的示例以了解问题) - 您应该检查空条件",然后调用 DbMigrator,然后进行自己的初始化.我使用了一个初始化器作为基类,并合并了另一个.

Both things are effectively 'initializers' - so you'd need to integrate them into one, that'd chain things in some way. Have in mind that order of execution is important (see above ex. for problems) - you should check for 'empty condition', then call DbMigrator, then do your own intialization. I used one initializer as a base class, and merged the other.

如果您只想seed - 您可以使用迁移配置,如果可行的话,这是最简单的.

And if you just want to seed - you can use the migration Configuration, that's the simplest if plausible.

非常开放式",没有单一的答案.通常它可以工作,但问题是expexted ...

Is pretty 'open ended' and there is no single answer. Usually it works, but issues are expexted...

  • 迁移是 3 件事(在我看来) - 您的代码模型/实体、您的数据库/表和数据库中的 __MigrationHistory 系统表.所有 3 个都需要保持 同步.如果您不同步",您可以删除迁移表,重新创建迁移(使用保留现有数据库的标志),然后像以前一样继续前进 - 即有针对何时使用实时数据的解决方案.为此,请参阅 如何忽略EF 4.3 迁移中的表/类

  • 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 stay in 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,

您需要在移动数据库时删除/创建 Db 的权限,

you'd need permissions to drop/create Db for when moving database,

确保您的连接正确(更改配置 - 并与您的 DbContext 名称或 ctor 同步),

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.,

不要混合数据库/代码版本 - 即一个代码实体版本 - 一个数据库.如果您想与不同的代码版本(例如暂存、生产)共享同一个 Db - 不要(EF6 中将提供多租户解决方案 - 例如 this),

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),

如果您需要手动应用数据库 - 通过 Update-Database 生成 script - 并应用它,请不要手动操作,否则您会弄错(迁移历史表) - 请参阅 这个,

if you need to manually apply the database - generate the script via Update-Database - and apply that, don't do things manually or you'll get it wrong (the migration history table) - see this one,

...仅举几例.它非常稳定且可用 IMO - 但如果您遵守规则 - 并知道限制是什么.

...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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆