目前我有一个基于Winforms的客户端(使用SQL Server),可以使用ClickOnce在桌面上交付和更新。
当前版本首先使用Entity Framework 4(基于ObjectContext)和数据库。当数据库架构发生变化时,我对客户端进行更新的方式是一个四步过程:
我发现这个过程对我们来说有点麻烦,对于客户来说更好,他们可以在他们认为合适的时候进行更新,并且不会因他们中间的更新而中断工作(可能涉及让客户在他们面前不想等待软件更新)。
现在我已经使用EF6和代码优先进行了几乎完全重写的客户端(仍然是Winforms)和迁移。
我一直在寻找文档但却找不到任何东西(这些天似乎只有网络编程,通常可以同时对数据库和Web客户端进行更新而不会中断用户),但是一旦我在生产中应用迁移,非更新的客户端就无法再使用数据库了。如果上下文与数据库模式不一致,EF会在实例化上下文时抱怨并抛出异常。
具体问题:有没有办法让EF6代码优先dbcontext与数据库模式的新迁移一起使用,只要它是兼容的?如果是这样的话,我可以继续做我到目前为止所做的事情。
一个(我猜)基于意见的问题,如果有人想扩展实际答案:有没有更好的方法来处理这种情况?我确定我不是唯一一个遇到这个问题的人,但Google提供的文档关键词过于广泛,到目前为止,我的搜索只出现了网络方案。
我目前处于客户端重写的阶段,可以允许进行重大更改,因此我不在乎解决方案是否会使代码的某些部分复杂化
答案 0 :(得分:3)
当应用程序初始化模型数据库时,通过直接调用DbContext.Database.Initialize
或实例化第一个DbContext
来执行,它会检查应用程序中的模型和数据库中的模型是否匹配。
为此,它计算模型哈希,并将其与存储在__MigrationHistory
表中的哈希(或EdmMetadata
表中的哈希值进行比较,如果它是从EF 4.x更新的)。这是在System.Data.Entity.Internal.ModelCompatibilityChecker.CompatibleWithModel
方法中完成的,该方法接收名为throwIfNoMetadata
的参数,该参数在内部实现中恰好是false
,因此如果没有元数据则不会抛出异常。
因此,如果在初始化数据库之前以某种方式使这些表消失,则可以避免错误。重要的是,您必须在不使用DbContext
的情况下进行此更改。如果没有,数据库将尝试初始化,如果此表存在,它将失败。因此,您可以使用普通ADO.NET
删除表格。
考虑到可以自动创建元数据表,例如通过应用迁移。
您还可以使用ctx.Database.CompatibleWithModel(true)
检查数据库元数据是否存在且是否兼容,以便将其删除。该参数恰好是我在上面提到的throwIfNoMetadata
。
默认数据库初始化程序为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();
}
}