c# - 使桌面客户端无需更新即可使用较新的 EF 数据库迁移

标签 c# entity-framework desktop-application entity-framework-migrations

目前我有一个 ERP,它是一个基于 Winforms 的客户端(带有 SQL Server),它使用 ClickOnce 在桌面上交付和更新。

当前版本首先使用 Entity Framework 4(基于 ObjectContext)和数据库。当数据库模式发生变化时,我对客户端进行更新的方式是一个四步过程:

  1. 在生产中使用兼容的列创建一个中间更新的数据库模式(允许在任何地方为 null 或具有默认值等)。老客户可以连接到该数据库并继续工作,就好像什么都没有改变一样
  2. 将桌面客户端更新到具有更新功能的中间版本,该更新功能说明了该中间架构但具有所有“最终架构”功能
  3. 更新所有客户端并且所有记录都与“最终”模式兼容后,使用所需的数据库约束对模式进行新更新
  4. 将所有客户端更新到映射到此最终模式的最终版本(它解释了数据库约束错误,并且需要这些模式更改才能工作)。

我发现这个过程虽然对我们来说有点麻烦,但对客户来说更好,他们可以在他们认为合适的时候更新,并且不会在工作中途被更新打断(这可能涉及让不想等待软件更新的客户出现在他们面前)。

现在我几乎完全重写了客户端(仍然是 Winforms),使用 EF6 和代码优先,并进行了迁移。

我一直在搜索文档,但找不到任何东西(现在似乎只有网络编程,通常可以同时更新数据库和网络客户端,而不会打扰用户),但是一旦我应用了迁移在生产环境中,未更新的客户端无法再使用数据库。如果实例化上下文未与数据库架构保持同步,EF 将在实例化上下文时提示并抛出异常。

具体问题:有没有一种方法可以让 EF6 代码优先 dbcontext 与更新的数据库架构迁移一起工作,而不是编译的迁移,只要它兼容?如果是这样的话,我可以继续做我目前正在做的事情。

如果有人想扩展实际答案,还有一个(我猜)基于意见的问题:有没有更好的方法来处理这种情况?我敢肯定我不是唯一遇到此问题的人,但是 Google 文档所需的关键字太宽泛,到目前为止,我的搜索中只出现了网络场景。

我目前正处于允许进行重大更改的客户端重写阶段,因此我不关心该解决方案是否会使部分代码复杂化

最佳答案

当应用程序通过直接调用 DbContext.Database.Initialize 或实例化第一个 DbContext 来初始化模型数据库时,它会检查应用程序中的模型和数据库中的模型匹配。

为此,它计算模型哈希,并将其与存储在 __MigrationHistory 表(或 EdmMetadata 表中,如果它是从英孚 4.x)。这是在 System.Data.Entity.Internal.ModelCompatibilityChecker.CompatibleWithModel 中完成的方法,它接收一个名为throwIfNoMetadata的参数,在内部实现中恰好为false,因此如果没有元数据则不会抛出异常。

因此,如果您在数据库初始化之前以某种方式让这些表消失,就可以避免错误。重要的一点是您必须在不使用 DbContext 的情况下进行此更改。如果不存在,数据库将尝试初始化,如果该表存在,则初始化失败。因此,您可以使用简单的 ADO.NET 删除表格。

考虑到可以自动创建元数据表,例如通过应用迁移。

您还可以使用 ctx.Database.CompatibleWithModel(true) 检查数据库元数据是否存在以及是否兼容,以摆脱它。参数就是我上面提到的throwIfNoMetadata

数据库初始化程序中的兼容性检查:

默认的 DB Initializer 是 CreateDatabaseIfNotExists,它会检查模型与 throwIfNoMetadata 设置为 false 的兼容性。这就是此解决方案有效的原因。但是,如果您实现自己的不运行检查的 DB Initializer 版本,它应该可以工作。

public virtual void InitializeDatabase(TContext context)
{
    Check.NotNull(context, "context");

    var existence = new DatabaseTableChecker().AnyModelTableExists(context.InternalContext);

    if (existence == DatabaseExistenceState.Exists)
    {
        // If there is no metadata either in the model or in the database, then
        // we assume that the database matches the model because the common cases for
        // these scenarios are database/model first and/or an existing database.
        if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false, existenceState: existence))
        {
            throw Error.DatabaseInitializationStrategy_ModelMismatch(context.GetType().Name);
        }
    }
    else
    {
        // Either the database doesn't exist, or exists and is considered empty
        context.Database.Create(existence);
        Seed(context);
        context.SaveChanges();
    }
}

关于c# - 使桌面客户端无需更新即可使用较新的 EF 数据库迁移,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35934762/

相关文章:

c# - 使用字符和数字对 List<> 进行排序

c# - self 跟踪实体的目的是什么?

c# - 如何在另一个项目中分离 Entity Framework

c# - QuickBooks SDK - 如何获取自定义字段和职位?

java - Java 桌面应用程序的局限性?

c# - && 运算符的行为类似于 ||运算符(operator)

c# - 单元测试 SQLite 存储库生成“无此类表”错误

c# - 使用EventFlow监控本地机器上的ETW事件

c# - 两个相似的 LINQ 查询,生成的 SQL 完全不同

java - 如何以及在何处存储 Java 桌面应用程序的主密码