如何使用 Entity Framework Code First 让我的数据库播种?

Posted

技术标签:

【中文标题】如何使用 Entity Framework Code First 让我的数据库播种?【英文标题】:How can I get my database to seed using Entity Framework CodeFirst? 【发布时间】:2011-09-12 19:55:21 【问题描述】:

数据库已成功创建(与表一样),但未播种。我花了几个小时阅读了大量的文章,但一直没能得到它。有什么建议吗?

附带说明一下,是否可以调用初始化程序而无需在客户端中引用我的 DatabaseContext?

我已经包含了我能想到的所有相关代码。如果还有什么有用的,请告诉我。

我尝试过的事情:

    我删除了我的连接字符串(因为无论如何它默认为 sqlexpress,只是名称已更改) 我将 DropCreateDatabaseIfModelChanges 更改为 DropCreateDatabaseAlways,还是一样。

编辑:真正奇怪的是它曾经工作过,但我不知道它是如何或为什么再次坏掉的。我假设连接字符串,但谁知道呢。

DatabaseInitializer.cs

public class DatabaseInitializer : DropCreateDatabaseIfModelChanges<DatabaseContext>

  protected override void Seed(DatabaseContext context)
  
    // Seeding data here
    context.SaveChanges();
  

DatabaseContext.cs

public class DatabaseContext : DbContext

  protected override void OnModelCreating(DbModelBuilder mb)
  
    // Random mapping code
  

  public DbSet<Entity1> Entities1  get; set; 
  public DbSet<Entity2> Entities2  get; set; 


Global.asax.cs - Application_Start()

protected void Application_Start()

  Database.SetInitializer<DatabaseContext>(new DatabaseInitializer());
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);

客户端 web.config

<connectionStrings>
  <add name="DatabaseContext" connectionString="data source=.\SQLEXPRESS;Database=Database;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
</connectionStrings>

解决方案

为了文档,我在这里分享我的解决方案。无论如何,导航所有的 cmets 都会很痛苦。最后,我在不同的类中有 DatabaseInitializer 和 DatabaseContext。虽然这些微小的变化修复了它,但我真的不明白,但就是这样。

DatabaseInitializer.cs

public class DatabaseInitializer : CreateDatabaseIfNotExists<DatabaseContext>

  protected override void Seed(DatabaseContext context)
  
    // Seed code here
  

DatabaseContext.cs

public class DatabaseContext : DbContext

  public DatabaseContext() : base("MyDatabase")  

  protected override void OnModelCreating(DbModelBuilder mb)
  
    // Code here
  

  public DbSet<Entity> Entities  get; set; 
  // Other DbSets

Global.asax.cs - Application_Start()

protected void Application_Start()

  Database.SetInitializer(new DatabaseInitializer());
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);

【问题讨论】:

您是否将种子项添加到您的数据上下文中?我知道我以前错过了。 是的,我是。感谢您的检查:)。 =D 不用担心。你能在代码中设置一个断点并确保你的 Seed 被调用吗? 没有调用 Seed 方法。 DatabaseInitializer 是。我错过了什么吗? 这真的很奇怪。尝试将 Database.SetInitializer 放入 DatabaseContext 的构造函数中。另外,尝试添加 base.Seed (context);到你的种子方法。我不知道为什么这很重要,但值得检查。编辑:我看到了您对@feanz 答案的评论,但您可以将 Database.SetInitializer 放入 datacontext 的构造函数中。我 acutlaly 将我的初始化程序类 inside 我的 datacontext 类。 【参考方案1】:

您的示例中的种子事件只会被触发一次,因为您使用 DropCreateDatabaseIfModelChanges 您可以将其更改为 DropCreateDatabaseAlways 我认为它应该每次都触发种子事件。

编辑

这是我的数据上下文

public WebContext()
   
    DbDatabase.SetInitializer(new DropCreateDatabaseIfModelChanges<WebContext>());

【讨论】:

我尝试过切换。没有运气。仍然创建了数据库和所有表,但没有种子。 我调用 Database.SetInitializer(new DatabaseInitializer());在适用于我的 DatabaseContext 的构造函数中 我不能在 DatabaseContext 中引用 DatabaseInitializer,因为那将是一个循环引用。此外,它不需要进入 DatabaseContext 构造函数,因为它应该由 App_Start 中的 DatabaseInitializer 构造函数调用(据我的理解)。【参考方案2】:

这就是我的 DbContext 类的所有外观,它们的种子很好:

public class MyDbContext : DbContext

    public DbSet<MyClass> MyClasses  get; set; 

    protected override void OnModelCreating (DbModelBuilder modelBuilder)
    
        base.OnModelCreating (modelBuilder);
        modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention> ();

        // Add any configuration or mapping stuff here
    

    public void Seed (MyDbContext Context)
    
        #if DEBUG
        // Create my debug (testing) objects here
        var TestMyClass = new MyClass ()  ... ;
        Context.MyClasses.Add (TestMyClass);
        #endif

        // Normal seeding goes here

        Context.SaveChanges ();
    

    public class DropCreateIfChangeInitializer : DropCreateDatabaseIfModelChanges<MyDbContext>
    
        protected override void Seed (MyDbContext context)
        
            context.Seed (context);

            base.Seed (context);
        
    

    public class CreateInitializer : CreateDatabaseIfNotExists<MyDbContext>
    
        protected override void Seed (MyDbContext context)
        
            context.Seed (context);

            base.Seed (context);
        
    

    static MyDbContext ()
    
        #if DEBUG
        Database.SetInitializer<MyDbContext> (new DropCreateIfChangeInitializer ());
        #else
        Database.SetInitializer<MyDbContext> (new CreateInitializer ());
        #endif
    

我已经使用过这种模式几次,对我来说效果很好。

【讨论】:

这不是我完全实现的解决方案,我在上面的原始问题中详细说明了我的解决方案。只是为了方便未来的用户使用而发布此评论。感谢 jdangelo 的所有帮助! 这是一个很好的方法,因为它不需要对 Application_Start() 进行任何更改,因此可以重复使用【参考方案3】:

这只是在我发现 Code First 功能时发生在我身上。当您首次使用 Code First 生成数据库而没有任何初始化策略时,通常会发生这种情况。

如果您决定稍后通过实施基于 DropCreateDatabaseIfModelChanges 的策略来执行此操作,但不修改您的模型,那么您的 Seed 方法将不会被调用,因为数据库生成并且您的策略只会在下次应用你改变你的模型。

如果您遇到这种情况,请尝试稍微修改您的模型以测试此假设,我敢打赌,您的数据库将被填充;)

我还没有解决方案,除了使用总是生成数据库的策略,但我真的不喜欢将初始化策略放在你的 DbContext 中,因为这个类将在你身上使用生产环境,虽然初始化策略似乎主要用于流畅的开发环境。

【讨论】:

【参考方案4】:

即使在Application_Start 中正确调用Database.SetInitializer,我的Seed 方法也没有被调用...原因很简单:如果您还没有任何代码,则可能根本不会调用初始化程序实际使用数据库上下文。

【讨论】:

【参考方案5】:

Global.asax 文件中的以下更改对我有用:

旧代码:

    protected void Application_Start()
    
        Database.SetInitializer<mycontextclassname>(new DropCreateDatabaseAlways<mycontextclassname>());             
       ...
    

新代码:

    protected void Application_Start()
    
        Database.SetInitializer<mycontextclassname>(new DropCreateDatabaseAlways<mycontextclassname>()); 
        Database.SetInitializer(new Initializer()); 
        ...
    

【讨论】:

【参考方案6】:

我刚遇到这个问题。我已经从 Web.config 文件中删除了“connectionstrings”部分,并且当前应用程序开始运行 - 没有 connectionstrings 部分!我重新添加该部分,并且数据库不再播种。这不是一个合适的解决方案,但我只是在这里添加一个数据点来解决问题。

幸运的是,它只是一个小的“一次性”应用程序,无论如何我很快就会丢弃......

【讨论】:

【参考方案7】:

这是我悲伤的小故事。

首先,经验教训:

    在使用上下文之前不会调用种子方法。 Global.asax.cs 在第一次运行时不会遇到断点,因为它在附加调试器之前运行。打断点 Global.asax.cs,您可以在 Web.config 和 点击一页;那么它就会被击中。 如果有 VS 连接 到分贝,播种不会发生。应用会抛出错误。

所以,为了避免悲伤:

断开 VS 连接。 一次性切换基类 DropCreateDatabaseAlways。 点击使用上下文的页面。

现在,悲伤:

    我的 Global.asax.cs 文件中有我的自定义 Initializer 类。我的 Initializer Seed 方法有一个断点;我启动了应用程序,但该方法从未受到影响。 :( 我在 Application_Start 中的 Database.SetInitializer 调用中设置了一个断点。那从来没有受到打击。 :( 我意识到我没有更改数据库架构,因此我将 DropCreateDatabaseIfModelChanges 更改为 DropCreateDatabaseAlways。依然没有。 :( 我终于找到了一个使用上下文的页面,并且它有效。 :/

【讨论】:

这很有效,感谢您提供这些有用的信息。我关闭了数据库连接,然后将基类切换为 DropCreateDatabaseAlways。效果很好。 全局只运行第一次。所以关闭IISExpress。在全局设置断点并运行。因为它的 IIS 应用程序级别,它会命中它一次。【参考方案8】:

我也很难让 Seed() 被调用。我非常感谢上面所有有用的建议,并且在使用 DropCreateDatabaseAlways 时遇到了一些运气......但并非总是如此!!

最近,我在我的 Repository 的构造函数中添加了以下代码行,效果很好:

    public CatalogRepository()
    
        _formCatalog.FormDescriptors.GetType();

    

触发 Seed() 被调用就足够了。如果您已经尝试了此答案之上的所有内容但仍然没有运气,请尝试一下。祝你好运,这真的是一次耗时的经历。

【讨论】:

【参考方案9】:

您可以调用update-database 手动运行Configuration 类中的种子方法。这需要enable-migrations 也处于开启状态。

PM> update-database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
No pending code-based migrations.
Running Seed method.

internal sealed class Configuration : DbMigrationsConfiguration<ProjectManager.Data.Database.ProjectDb>

    public Configuration()
    
        AutomaticMigrationsEnabled = false;
    

    protected override void Seed(ProjectManager.Data.Database.ProjectDb context)
    
        context.Status.AddOrUpdate(
            new Status()  Id = 1, Text = "New" ,
            new Status()  Id = 2, Text = "Working" ,
            new Status()  Id = 3, Text = "Completed" ,
            new Status()  Id = 4, Text = "Skipped" 
        );
    

【讨论】:

【参考方案10】:

更新说明此答案不正确!我的数据库没有被播种的原因仍然是个谜(但这不是缺少默认的基本构造函数调用,正如@JaredReisinger 所指出的)

我很欣赏这个问题有点老了,但我最终来到了这里,所以其他人可能会这样做。这是我的 tuppence 价值:

即使我删除了数据库并使用 DropDatabaseInitialiser 重新启动,我的数据库也可以正常创建但没有播种。

阅读上面的代码后,我注意到我的上下文构造函数是这个

public MyApp_Context()

    // some code

而上面的示例对于我的设置如下所示

public MyApp_Context() : base("name=MyApp_Context")

    // some code

是的,我没有调用基础对象的构造函数!在这种情况下,除了播种之外,我没有预料到所有事情都可以工作,但这似乎是(可重复的)情况。

注意,我实际上不需要在基本构造函数调用中提供上下文名称;我最初只是这样写的,因为我正在复制上面解决方案的格式。所以我的代码现在是这样的,并且播种在初始数据库创建时起作用。

public MyApp_Context() : base()

    // some code

【讨论】:

只要您不直接调用它的特定变体,就会自动调用基类的默认构造函数。有关详细信息,请参阅***.com/questions/13166019/…。 是的,我必须同意你的看法。唯一的事情是,当我三年前写这篇文章时,我似乎已经注意到省略(冗余) base() 调用与包含它会导致不同的结果。我可能会在某个时候挖掘出该代码,但最安全的做法可能是假设那里还有其他一些影响。【参考方案11】:

仔细确保您没有多次声明上下文变量。如果在播种后再次声明,则种子会被覆盖。

【讨论】:

【参考方案12】:

我遇到了同样的问题,在更改 Global.asax 文件和 Intializer 文件后它工作了。我希望它对那些仍然有数据播种问题的人有用。

Global.asax 中的新代码:

    protected void Application_Start()
    
        Database.SetInitializer<mycontextclassname>(new DropCreateDatabaseAlways<mycontextclassname>()); 
        Database.SetInitializer(new Initializer()); 
        ...
    

初始化器文件代码:

public class Initializer : System.Data.Entity.DropCreateDatabaseAlways<Context>

【讨论】:

以上是关于如何使用 Entity Framework Code First 让我的数据库播种?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Entity Framework 7 记录查询?

Entity Framework 学习系列 - 认识理解Entity Framework

如何使用 Entity Framework Core 模拟异步存储库

如何使用 Entity Framework 生成和自动递增 Id

如何使用 Entity Framework Core 正确保存 DateTime?

如何使用 Entity Framework Core 获取主键值