EF6 - 在没有种子的情况下运行更新数据库命令

Posted

技术标签:

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

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

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


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

【问题讨论】:

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

【讨论】:

以上是关于EF6 - 在没有种子的情况下运行更新数据库命令的主要内容,如果未能解决你的问题,请参考以下文章

如何运行配置类迁移的 Seed() 方法

在 EF6 中重新加载导航属性

在没有 Laravel 的情况下使用 Laravel 数据库迁移和种子 [关闭]

连接更新数据库命令的EF6问题

EF6 事务

EF命令行工具 migrate.exe 进行Code First更新数据库,6.3+使用ef6.exe