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

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

问题描述

我有一个自定义DatabaseInitialiser,低于

  ///<总结> 
///实现的IDatabaseInitializer提供上下文定制的数据库初始化。
///< /总结>
///< typeparam NAME =TContext> TContext是的DbContext< / typeparam>
公共类ParikshaDataBaseInitializer< TContext> :IDatabaseInitializer< TContext>其中,TContext:的DbContext
{
///<总结>
///初始化数据库的方法。
///负责数据库不能被丢弃,因为它是在使用中的问题,同时删除并重新创建数据库。
///< /总结>
///< PARAM NAME =背景>所述的DbContext在其上运行的初始化器< /参数>
公共无效InitializeDatabase(TContext上下文)
{
变种存在= context.Database.Exists();


{
如果(存在和放大器;&安培; context.Database.CompatibleWithModel(真))
{
//什么都好,我们完成
的回报;
}

如果(存在!)
{
context.Database.Create();
}
}
赶上(例外)
{
//东西是错误的,要么我们无法找到元数据或模型是不兼容的。
如果(存在)
{
context.Database.ExecuteSqlCommand(改变与ROLLBACK立即数据库Pariksha SET SINGLE_USER);
context.Database.ExecuteSqlCommand(使用主DROP DATABASE Pariksha);
context.SaveChanges();
}

context.Database.Create();
}
}
}



一些关于上面的代码不仅仅是哈克(随意凑钱的帮助)



然后我加入迁移,并得到了迁移脚本,以正常工作为好。

 内部密封类配置:DbMigrationsConfiguration< ParikshaContext> 
{
公共配置()
{
AutomaticMigrationsEnabled = FALSE;
ContextKey =EFRepository.Context.ParikshaContext;
}

保护覆盖无效种子(ParikshaContext上下文)
{
}
}



迁移正常工作。



现在的问题是在我的应用程序启动时我该怎么办?
这样的事情

  VAR配置=新配置(); 
变种迁移=新DbMigrator(配置);
migrator.Update();

和一些论坛,因为我不希望这似乎有些奇怪的构造提出这个问题,以及检查数据库和模式是正确的,每次我使用的上下文。那么,有什么能可能使用这种技术还是我得到的建议是错误的语境?



 公共ParikshaContext( ):基座(Pariksha)
{
Database.SetInitializer(新ParikshaDataBaseInitializer&所述; ParikshaContext>());
}

要总结,




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


  2. 这将是理想的策略,让该移民在所有条件下工作,当我们移动数据库从一个环境到另一个?



解决方案

这是我在分贝初始化程序它结合了迁移初始化和默认的<$ C $尝试C>分贝创建一个用播种。 (注意:这不是理想的,更像是一个简单的练习,但给出了一个解决方案,你问什么在这里,大部分的作品 - 只是检查我所做的所有更新)。




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




对于为什么如何 - 充分了解,我建议你也咨询的 EF源代码(这是新版本,但在很多方面是相似的)



1)



)分贝初始化被调用,通常只有一次(每个连接) - 当您尝试访问您的样板的第一次(第一查询或类似)。把一个断点在你的初始化检查。



所以这是完全安全的把它构造函数中(虽然我更喜欢它在启动时某个地方,也配置)。 它只是变得时的需要它来初始化(和最后一组时),你不应该手动调用它调用。



总之,迫使你可以做 this.Database.Initialize初始化(力:真正的);




有关切换连接时看到我对问题_
后的代码第一个自定义的连接字符串和迁移而无需使用IDbContextFactory




b)如果您创建自己的 IDatabaseInitializer ,你还是要迁移到工作并排



您不应该只是叫 DbMigrator 从外部 - 为您的自定义初始化将错过整个DB创造(例如,如果你希望种子或东西 - 检查我上面的例子)。



这两种东西都是有效的初始化 - 所以你需要将它们集成到一个,那简直在某种程度上链的东西。有记住,执行的顺序很重要(见上文前的问题。) - 你应该检查'空状态',然后调用 DbMigrator ,然后做自己的intialization。我使用的一个初始化作为基类,并合并其他。



如果你只是想种子 - 您可以使用迁移配置,这是最简单的,如果合理的。



2)



是相当开放结束了,并没有一个统一的答案。通常它的工作原理,但问题expexted ...




  • 迁移是三样东西(在我看来) - 你的代码模型/实体,你的数据库/表和数据库的 __ MigrationHistory 系统表。所有3需要保持同步。如果你不同步,你可以删除迁移表,重新迁移(有标记,以保持现有的数据库),然后像以前一样继续前进 - 即没有用于实时数据时的解决方案。对于此看到如何忽略在EF表/ 4.3类迁移


  • 你需要的权限删除/创建分贝移动数据库时,


  • 请确保您的连接是正确的(改变配置 - 在你的DbContext名称或构造函数同步)


  • 保持简单,不要做花哨的东西或交换机连接的代码(可能的,但有问题)等,


  • 不要混合数据库/代码版本 - 即一个代码实体版本 - 一个数据库。如果你想分享不同的代码版本相同的DB(如分期,生产) - 不(多租户解决方案将在EF6可 - 如的这个


  • 如果您需要手动应用数据库 - 生成剧本通过更新,数据库 - 和应用是,不要手工做的事情,否则你会弄错了(迁移历史表) - 见的这个




...这只是仅举几。这是相当稳定和可用IMO - 但如果你遵守规则 - 而知道限制是什么






 类CreateAndMigrateDatabaseInitializer< TContext,TConfiguration> 
:CreateDatabaseIfNotExists< TContext>中IDatabaseInitializer< TContext>
式TContext:的DbContext
式TConfiguration:DbMigrationsConfiguration< TContext>中新()
{
私人只读DbMigrationsConfiguration _configuration;
公共CreateAndMigrateDatabaseInitializer()
{
_configuration =新TConfiguration();
}
公共CreateAndMigrateDatabaseInitializer(字符串连接)
{
Contract.Requires(string.IsNullOrEmpty(连接),连线!);

_configuration =新TConfiguration
{
TargetDatabase =新DbConnectionInfo(连接)
};
}
无效IDatabaseInitializer< TContext>!.InitializeDatabase(TContext上下文)
{
VAR doseed = context.Database.Exists();
//&放大器;&安培;新DatabaseTableChecker()AnyModelTableExists(上下文)。
//检查,看看是否种子 - 我们缺乏'的'AnyModelTableExists'
// ...可以复制/如果需要的话做,否则......

变种迁移=新DbMigrator(_configuration);
//如果(doseed || context.Database.CompatibleWithModel(假)!)
如果(migrator.GetPendingMigrations()任何()。)
migrator.Update();

//使用'CreateDatabaseIfNotExists前进的'种子'
base.InitializeDatabase(背景);
如果(doseed)
{
种子(背景);
context.SaveChanges();
}
}
保护覆盖无效种子(TContext上下文)
{
}
}


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,

  1. what is the correct use-case for the different techniques available ?

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

How to create initializer to create and migrate mysql database?

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)

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 IDbContextFactory

b) If you create your own IDatabaseInitializer and you still want migrations to work side 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 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.

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

  • 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 via Update-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屋!

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