【问题标题】:EF6 - Run Update-Database Command without seedsEF6 - 在没有种子的情况下运行更新数据库命令
【发布时间】:2015-04-22 12:13:51
【问题描述】:

我正在使用 Entity Framework 6 并且正在使用迁移。我已经使用初始迁移创建了数据库。现在我已经对模型进行了更改,上下文也发生了变化,我想更新数据库但是...... 当我尝试再次运行Database-Update 命令时,种子也在运行,这会由于再次插入一些数据而带来错误。

那么,如何在不运行种子方法的情况下运行Update-Database 命令?


很难相信 EF 没有像 -No-Seed 这样的简单选项。我几乎与其他 ORM 一样安全。

【问题讨论】:

  • 你使用的是AddOrUpdate扩展方法吗?
  • 不,我没有使用AddOrUpdate 进行所有种子插入,除了原始的 slq 脚本。
  • 同意,这是缺失的,并不是因为我们做错了什么,EF 的 update-database 命令只是缺少一个 [no-seed] 参数。有时我想通过不需要重新播种数据的迁移来匆忙通过 3 或 4 个环境。我们的种子方法可能需要 2-3 分钟才能针对我们的远程数据库运行。 : (

标签: c# .net entity-framework migration database-migration


【解决方案1】:

来自DbMigrationsConfiguration<TContext>的源码:

/// <summary>
    /// Runs after upgrading to the latest migration to allow seed data to be updated.
    /// 
    /// </summary>
    /// 
    /// <remarks>
    /// Note that the database may already contain seed data when this method runs. This means that
    ///             implementations of this method must check whether or not seed data is present and/or up-to-date
    ///             and then only make changes if necessary and in a non-destructive way. The
    ///             <see cref="M:System.Data.Entity.Migrations.DbSetMigrationsExtensions.AddOrUpdate``1(System.Data.Entity.IDbSet{``0},``0[])"/>
    ///             can be used to help with this, but for seeding large amounts of data it may be necessary to do less
    ///             granular checks if performance is an issue.
    ///             If the <see cref="T:System.Data.Entity.MigrateDatabaseToLatestVersion`2"/> database
    ///             initializer is being used, then this method will be called each time that the initializer runs.
    ///             If one of the <see cref="T:System.Data.Entity.DropCreateDatabaseAlways`1"/>, <see cref="T:System.Data.Entity.DropCreateDatabaseIfModelChanges`1"/>,
    ///             or <see cref="T:System.Data.Entity.CreateDatabaseIfNotExists`1"/> initializers is being used, then this method will not be
    ///             called and the Seed method defined in the initializer should be used instead.
    /// 
    /// </remarks>
    /// <param name="context">Context to be used for updating seed data. </param>

基本上,除了实现“添加或更新”逻辑之外,您别无选择,因为每次使用初始化程序后都会执行 Seed 方法。

AddOrUpdate 扩展方法对此很有用,但我在某些情况下也使用过:

            if (!context.Entities.Any())
            {
                 // Seed
            }

【讨论】:

  • 是的...我要继续删除我写一半的答案,因为您的代码示例正是我所写的:)... +1
  • 如果没有待处理的迁移就不会执行 Seed 方法是完全错误的
  • @Colin 你是对的。在执行“migrator.Update()”之前,我的代码“if (migrator.GetPendingMigrations().Any())”中有这个。我正在更新我的答案。谢谢你。
【解决方案2】:

从此页面:Database initializer and Migrations Seed methods

Configuration 类上的 Seed 方法在 Update-Database PowerShell 时运行 命令被执行。除非正在使用 Migrations 初始化程序 当你的应用程序不会执行 Migrations Seed 方法 开始。

所以,我认为你在这里没有太多选择,如果你运行Update-Database 命令,迁移Seed 方法总是会被调用。我正在挖掘是否存在此命令的参数,让您指定不运行 Seed 方法,但我担心它还不存在。这些是您可以使用的所有参数(您可以在此link 中找到更多信息):

Update-Database [-SourceMigration <String>] [-TargetMigration <String>] [-Script] [-Force] 
  [-ProjectName <String>] [-StartUpProjectName <String>] [-ConfigurationTypeName <String>] 
  [-ConnectionStringName <String>] [-AppDomainBaseDirectory <String>] [<CommonParameters>]

Update-Database [-SourceMigration <String>] [-TargetMigration <String>] [-Script] [-Force] 
  [-ProjectName <String>] [-StartUpProjectName <String>] [-ConfigurationTypeName <String>] 
  -ConnectionString <String> -ConnectionProviderName <String> 
  [-AppDomainBaseDirectory <String>] [<CommonParameters>]

如果您正在执行 sql 脚本以在 Seed 方法中插入数据,则可以使用布尔条件来避免在第一次之后重新插入相同的字段。

作为附加信息,您的请求中有一个参数,以避免在您运行Update-Database 命令时执行Seed 方法,此site 中已经存在,EF 团队使用该参数收集来自社区的建议.

【讨论】:

    【解决方案3】:

    我将所有种子语句移动到单独的方法中,这些方法可以在运行“更新数据库”之前轻松注释掉。

    protected override void Seed(Tpsc.EDI.EDIContext context)
    {
        try
        {
            //EDI.Seed.DoSeed(context);
        }
        catch (DbEntityValidationException e)
        {
            ...
        }
    }
    

    【讨论】:

      【解决方案4】:

      如果您有用于插入数据的 sql 脚本,并且您想防止以后再插入,您可以使用 sql 合并 来避免重复。将所有数据插入到与目标表结构相同的临时表中,然后使用合并来决定何时插入记录。如果它们匹配是因为您插入了一次。

      假设 S 是包含所有数据的临时表,而 T 是最终表

      MERGE Target AS T
      USING Source AS S
      ON (T.EmployeeID = S.EmployeeID AND T.EmployeeName LIKE 'S%' 
          AND S.EmployeeName LIKE 'S%' )
      WHEN NOT MATCHED BY TARGET
          THEN INSERT(EmployeeID, EmployeeName) VALUES(S.EmployeeID, S.EmployeeName)
      WHEN MATCHED 
          THEN UPDATE SET T.EmployeeName = S.EmployeeName
      WHEN NOT MATCHED BY SOURCE
          THEN DELETE
      

      更多参考请使用https://technet.microsoft.com/en-us/library/bb522522(v=sql.105).aspx

      【讨论】:

        【解决方案5】:

        我通常使用命令update-database -sc 然后运行生成的脚本来手动更新数据库。 一开始我觉得这样做不太舒服,但现在我想在为时已晚之前看看我的数据库会发生什么。

        【讨论】:

          【解决方案6】:

          老问题,但总是有用的。所以我的贡献是:在您的 web.config/app.config 中使用 ConfigurationManager 选项

          这是可能的,因为 System.Configuration.ConfigurationManager 可以从您的 Configuration 类中访问。

          这样做:

          public sealed class Configuration : DbMigrationsConfiguration<Booking.EntityFramework.BookingDbContext> {
          
              public Configuration() {
                 // ...
              }
          
              protected override void Seed(Booking.EntityFramework.BookingDbContext context) {
                  // check configuration if seed must be skipped
                  if (!bool.Parse(ConfigurationManager.AppSettings["Database.Seed"] ?? bool.TrueString)) return;
          
                  // if here, seed your database
                  // ...
              }
          }
          

          通过这种方式,您可以在 web.config 或 app.config 文件中定义应用程序设置:

          <appSettings>
            <add key="Database.Seed" value="false" />  <!-- <== do not seed! -->
            ...
          </appSettings>
          

          【讨论】:

            猜你喜欢
            • 2014-09-06
            • 2021-12-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-04-15
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多