【问题标题】:How to bypass the System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method) validation of Entity framework?如何绕过实体框架的 System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method) 验证?
【发布时间】:2012-04-26 19:51:02
【问题描述】:

我正在使用自定义方法来跟踪 n 层断开连接实体类的单个修改属性。我从

中提取它

编程实体框架:DbContext by Julia Lerman 和 Rowan 米勒(奥莱利)。版权所有 2012 Julia Lerman 和 Rowan Miller, 978-1-449-31296-1。

代码是:

public void ApplyChanges<TEntity>(TEntity root) where TEntity : class, IObjectWithState {
        // bind the entity back into the context
        dbContext.Set<TEntity>().Add(root);
        // throw exception if entity does not implement IObjectWithState
        CheckForEntitiesWithoutStateInterface(dbContext);

        foreach (var entry in dbContext.ChangeTracker.Entries<IObjectWithState>()) {
            IObjectWithState stateInfo = entry.Entity;
            if (stateInfo.State == RecordState.Modified) {
                // revert the Modified state of the entity
                entry.State = EntityState.Unchanged;
                foreach (var property in stateInfo.ModifiedProperties) {
                    // mark only the desired fields as modified
                    entry.Property(property).IsModified = true;
                }
            } else {
                entry.State = ConvertState(stateInfo.State);
            }
        }
        dbContext.SaveChanges();
    }

此方法的目的是让 EF 知道只有一组预定义的实体字段已准备好在下次调用 SaveChanges() 时进行更新。这是为了解决实体在 ASP.NET MVC 3 中的工作方式,如下所示:

  • 在初始页面加载时:控制器的 Get 操作正在加载 实体对象并将其作为参数传递给视图。

  • 视图生成控件用于编辑实体的 2 个字段, 并在隐藏字段中保存记录的 ID。

  • 点击 [保存] 并将实体发送回控制器时 除了视图中保留的 3 个字段之外,其余字段中都带有 null 价值。这是 MVC 绑定管理器的默认行为。

如果我将更改保存回数据库,更新查询当然会用如下语句覆盖非映射字段:

UPDATE non_mapped_field_1 = NULL, ..., mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2', ... non_mapped_field_n = NULL WHERE ID = mapped_field_3

这就是我尝试单独跟踪字段并仅更新我感兴趣的字段的原因。在使用 ApplyChanges() 调用自定义方法之前,我正在添加我想要包含的字段列表更新 IObjectWithState.ModifiedProperties 列表,以获取 SQL 语句如下:

UPDATE mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2' WHERE id = mapped_value_3

问题是,在 ApplyChanges 中将其中一个字段标记为已修改时,即:

entry.Property(property).IsModified = true;

系统抛出以下异常

{System.InvalidOperationException: Member 'IsModified' cannot be called for property 'NotifyCEDeadline' on entity of type 'User' because the property is not part of the Entity Data Model.

at System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method)

at System.Data.Entity.Internal.InternalPropertyEntry.set_IsModified(Boolean value)

at System.Data.Entity.Infrastructure.DbPropertyEntry.set_IsModified(Boolean value)

...

所以问题是。有一种方法可以绕过这个 EF 验证,或者让上下文知道我正在尝试更改的这个系统属性 (IsModified) 的存在?

架构总结:

  • EF 代码优先(注解 + Fluent API)
  • Oracle .NET EF 数据提供程序 (ODAC)
  • 使用 nInject.MVC 将上下文注入到自定义业务上下文 => 这就是我自定义 ApplyChanges() 方法的原因

    使用 (var context = new BreakAwayContext()){ context.Set().Add(root);

    对已经初始化的 dbcontext 的简单调用

    dbContext.Set().Add(root);

  • Oracle 数据库是手动创建的,即没有 EF 的帮助,因此不使用 EF 元数据表。

谢谢, 伊万。

【问题讨论】:

  • 您对此有解决方案吗?我面临着类似的问题。

标签: asp.net-mvc entity-framework ef-code-first ninject.web.mvc odac


【解决方案1】:

很好的描述,但是我找不到任何信息说明为什么需要在对象中使用名为“IsModified”的瞬态属性和/或为什么需要告诉 EF 它正在被修改(EF 将无法无论如何都要坚持下去)。

如果属性包含在视图中,则 IsModified 属性的值应由模型绑定器设置。

您可以在 ApplyChanges 方法中添加代码以跳过名为“IsModified”的属性,或者更好的是,使用 entry.CurrentValues.PropertyNames 仅过滤已知属性,例如:

foreach (var property in stateInfo.ModifiedProperties) { 
    // mark only the desired fields as modified 
    if (entry.CurrentValues.PropertyNames.Contains(property)) {
        entry.Property(property).IsModified = true; 
    }
} 

更新:Ivan,很抱歉,当您几个月前发布该问题时,我没有更好地理解问题,并且在您添加这些澄清 cmets 后我没有跟进。我想我现在理解得更好了。也就是说,我认为我提供的代码 sn-p 可以成为解决方案的一部分。通过查看您再次遇到的异常,我现在了解到 EF 检测到的问题是 NotifyCEDDealine 不是持久属性(即它没有在 Code First 模型中映射到数据库中的列)。 IsModified 只能用于映射属性,因此您有两个选择:更改实体中 IObjectWithState 的实现代码,以便非映射属性不会记录在 ModifiedProperties 中,或者使用我的代码 sn-p 来防止调用IsModified 与那些。

顺便说一句,执行所有这些操作的另一种方法是使用 Controller.TryUpdateModel API 仅在您的实体中设置修改后的属性。

希望这会有所帮助(虽然我明白这已经很晚了)。

【讨论】:

  • IsModified 属性:这是一个内部属性,EF 主要用于了解哪些实体需要包含在下一个 SQL 命令中。调用 SaveChanges() 时调用的 EF 函数 DetectChanges() 会自动确定绑定到当前图形的哪些实体需要更新、插入、删除等。
  • 在 MVC 工作的断开连接的场景中,控制器的 post 操作无法知道在从 DB 初始检索后更改的各个属性,这确实是在不同的上下文中进行的采取行动。所以我们的默认模式是让 EF 知道 ENTITY 已更改并调用 SaveChanges(),生成对记录的完整更新。
  • 这会产生一个问题,因为在我们想要在数据库中更新之后,视图中的非映射属性将被设置为 NULL。我尝试使用此代码复制的技巧是取消将实体标记为已修改,然后选择性地将单个属性标记为已修改,以便 EF 生成仅包含修改后属性的更新语句,而不是整个集合。
  • 这应该可以工作,因为这段代码是由所描述的书中公认的编辑编写的,但似乎不适用于我的项目。如果您遵循堆栈跟踪序列,EF 将验证 IsModified 的集合访问者是否在模型中:|我真的不知道 EF 为什么要这样做,因为我不习惯在代码优先但自定义实体和字段中映射系统属性。如果你用谷歌搜索这个函数的名称 ValidateNotDetachedAndInModel() 除了这篇文章在搜索结果中。
  • 为了澄清您发布的与我发布的异常相关的代码,属性是“NotifyCEDeadline”(我在视图中跟踪的自定义字段/列之一)。 IsModified 是 EF API 的一部分。问候,伊万。
猜你喜欢
  • 2010-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-11
  • 2010-09-23
  • 2011-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多