如何使用Entity Framework CodeFirst使我的数据库进行种子生成?

62
数据库已成功创建(包括表),但未进行种子数据填充。我花了几个小时阅读了大量文章,但仍然无法解决问题。有什么建议吗?
另外,是否可以在客户端没有对我的DatabaseContext的引用的情况下调用初始化程序?
我已经包含了我能想到的所有相关代码。如果还有其他有用的信息,请让我知道。
我尝试过的事情:
1. 删除了连接字符串(因为它默认为sqlexpress,只是名称改变了) 2. 我将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; }

}

全局.asax.cs - 应用程序启动()
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>

解决方案

为了文档记录,我在这里分享我的解决方案。浏览所有评论真的很痛苦。最终,我将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 - 应用程序启动()

protected void Application_Start()
{
  Database.SetInitializer(new DatabaseInitializer());
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);
}

是的,我是。不过还是谢谢你确认一下 :)。 - Alec
1
放心。你能在代码中设置一个断点并确保你的Seed被调用了吗? - Jim D'Angelo
Seed 方法没有被调用。DatabaseInitializer 被调用了。我是不是漏掉了什么? - Alec
这真的很奇怪。尝试将Database.SetInitializer放在DatabaseContext的构造函数中。此外,尝试在种子方法中添加base.Seed(context);。我不知道为什么会有影响,但值得检查。编辑:我看到了你在@feanz答案上的评论,但是你可以将Database.SetInitializer放在数据上下文的构造函数中。实际上,我将我的初始化器类放在我的数据上下文类中。 - Jim D'Angelo
当您使用DatabaseContext构造函数或实际使用DatabaseContext进行调用时,它是否会触发Seed?无论我是在App_Start中还是在DatabaseContext构造函数中进行Database.SetInitializer调用,只有在我使用DatabaseContext进行调用时,Seed才会实际触发。 - Alec
显示剩余4条评论
12个回答

37

这就是我所有的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
    }
}

我已经多次使用过这种模式,它对我非常有效。


2
这不是我实际实现的解决方案,我已经在上面的原始问题中详细说明了我的解决方案。只是为了方便未来的用户而发布此评论。感谢jdangelo的所有帮助! - Alec
1
这是一个非常好的方法,因为它不需要对 Application_Start() 进行任何更改,因此可重复使用。 - xkingpin

10

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


9

这是我的悲伤故事。

首先,需要学到的教训:

  1. 只有在使用上下文时才会调用 seed 方法。
  2. 在第一次运行时,Global.asax.cs 不会触发断点,因为它在调试器附加之前运行。要在 Global.asax.cs 上触发断点,您需要将一些空格添加到 Web.config 中并访问一个页面; 然后它就会被触发。
  3. 如果 VS 连接到数据库,则不会进行种子操作。应用程序将抛出错误。

因此,要避免悲伤:

  • 断开 VS 的连接。
  • 将基类 DropCreateDatabaseAlways 切换为一次性使用。
  • 访问使用上下文的页面。

现在,让我们来感受一下悲伤:

  1. 我将自定义的 Initializer 类放在 Global.asax.cs 文件中。我在 Initializer Seed 方法上设置了一个断点;我启动应用程序,但该方法从未被触发。:(
  2. 我在 Application_Start 中指定了一个 Database.SetInitializer 的断点,但那也没有被触发。:(
  3. 然后我意识到我没有进行数据库模式更改,所以我将 DropCreateDatabaseIfModelChanges 更改为 DropCreateDatabaseAlways。但仍然没有结果。:(
  4. 最终我打开了一个使用上下文的页面,它奇迹般地工作了。:/

这个方法很有效,谢谢你提供这么有用的信息。我关闭了数据库连接,然后将基类切换为DropCreateDatabaseAlways。它运行得很好。 - Rasshme Chawla
全局只在第一次运行。因此关闭IISExpress。在全局中设置断点并运行。它会被命中一次,因为它是IIS应用程序级别的。 - Piotr Kula

3

您可以调用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" }
        );
    }
}

2
以下是我为您翻译的结果,涉及IT技术相关内容。请尽量按照原文格式进行排版。

以下是我使用的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()); 
        ...
    }

1
我也曾经遇到过调用Seed()的困难。我非常感谢上面所有有用的建议,并且使用DropCreateDatabaseAlways已经有了一些好运气...但并不总是如此!!
最近,在我的存储库构造函数中添加了以下代码行,效果很好:
    public CatalogRepository()
    {
        _formCatalog.FormDescriptors.GetType();

    }

这足以触发Seed()的调用。如果您已尝试了上面的所有方法仍然没有运气,请尝试一下。祝你好运,这真的是一个耗时的经历。


0

我刚遇到了这个问题。我从Web.config文件中删除了“connectionstrings”部分,目前应用程序已经开始运行 - 没有连接字符串部分!我重新添加了该部分,但数据库不再进行种子填充。这不是一个适当的解决方案,但我只是在这里添加一个数据点,以潜在地解决问题。

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


0

一定要仔细检查您是否声明了多个上下文变量。如果在填充数据后再次声明,填充的数据将被覆盖。


0

我在探索Code First功能时遇到了这个问题。当您首次使用Code First生成数据库而没有任何初始化策略时,经常会出现这种情况。

如果您稍后决定通过实现基于DropCreateDatabaseIfModelChanges的策略来进行初始化,但没有修改模型,则由于数据库生成和策略仅在下次更改模型时应用,因此不会调用您的Seed方法。

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

我还没有解决方案,除了使用始终生成数据库的策略,但是我真的不舒服将初始化策略放在您的DbContext中,因为这个类将在您的生产环境中使用,尽管初始化策略似乎主要用于流畅的开发环境。


0
您示例中的种子事件只会被触发一次,因为您正在使用 DropCreateDatabaseIfModelChanges。您可以将其更改为 DropCreateDatabaseAlways,我认为它应该每次都会触发种子事件。 编辑 这是我的 DataContext。
public WebContext()
{   
    DbDatabase.SetInitializer(new DropCreateDatabaseIfModelChanges<WebContext>());
}

我试过切换。没有运气。仍然创建了数据库和所有表,但是没有种子数据。 - Alec
我在DatabaseContext的构造函数中调用了Database.SetInitializer<DatabaseContext>(new DatabaseInitializer()),这对我很有效。 - Richard Forrest
我不能在DatabaseContext中引用DatabaseInitializer,因为那将会是一个循环引用。此外,它不应该需要放在DatabaseContext构造函数中,因为它应该由App_Start中的DatabaseInitializer构造函数调用(就我所知)。 - Alec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接