EF6 更新附加需要 IsModified 否则静默失败与 EF4

EF6 Update Attach Needs IsModified else Silent Fail vs EF4

你能告诉我为什么 Entity Framework 6 需要 "IsModified" 代码行来进行附加更新,否则我的代码将 "Silent Fail" vs Entity Framework 4?换句话说,在 Entity Framework 4 中,我使用附加功能进行更新。但是在 EF6 中,如果我做类似的事情,数据库将不会更新并且不会抛出异常(静默失败)。如果我将 "IsModified" 行放在我的代码中,它可以工作,但这是 unacceptable,因为开发人员可以将 "IsModified" 代码放在外面,更新将失败,而且没有人会知道。

在数据库中设置的以下条件下,此问题将 occur/not 出现在 EF6 中: 1.如果active设置为default 1且AllowNulls=false,更新失败 2.如果active没有设置为默认值,AllowNulls=false,更新失败 3. 如果未将活动设置为默认值且 AllowNulls=true,则更新有效 4. 如果 active 设置为默认值 1 且 AllowNulls=true,更新有效

这类似于:EntityFramework not saving null and false value 但不完全相同。我将引导您解决问题:

1) 如果你有 Visual Studio 2010,你可以继续,否则,你可以相信我,EF4 的工作原理与描述的一样。

在 Visual Studio 2010 年,新项目 ASP.NET MVC 2 Web 应用程序,使用 .NET Framework 4,并将其命名为 EF4Works。不要创建单元测试。

2)一定要加默认,不允许空值,很重要

    CREATE TABLE [dbo].[Document](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [DocumentName] [varchar](10) NULL,
    [Active] [bit] NOT NULL CONSTRAINT [DF_Document_Active]  DEFAULT ((1)),
 CONSTRAINT [PK_Document] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

3) 插入行 "DocName" 1(true)

4) 添加 edmx table.

5) 在 Home Index Controller 中(如有必要,添加此控制器)添加 运行 此代码(类似)并检查 DB 是否有效!(更新 DB)(在调用视图之前放置一个断点):

    public ActionResult Index()
    {
        BreazEntities entities = new BreazEntities();
        Document document = new Document { Id = 1 };
        entities.Documents.Attach(document);
        document.Active = false;
        entities.SaveChanges();

        return View();
    }

6) 将活动标志放回 1

7) 在 Visual Studio 2013 中添加新的解决方案和项目。Web,.NET Framework 4.5.1 ASP.NET WebApplication 称为 EF6Fails,下一个向导页面 MVC,将身份验证更改为无身份验证。在包管理器控制台中:uninstall-package EntityFramework -project ef6fails -force 然后 install-package EntityFramework -version 6.1.3 -project ef6fails

8) 将 ef6fails 中的 edmx 添加到文档 table.

9) 运行 此代码在控制器中并在调用视图之前放置一个断点:

    public ActionResult Index()
    {
        BreazEntities6 entities = new BreazEntities6();
        Document document = new Document { Id = 1 };
        entities.Documents.Attach(document);
        document.Active = false;
        entities.SaveChanges();

        return View();
    }
  1. 这不起作用。我将不得不执行以下操作,这是 unacceptable 因为新开发人员可能会排除以下内容并且不知道代码不起作用。有什么东西,我可以在全球范围内添加到解决方案中,而不是让开发人员添加以下内容吗?我会研究并尝试自己添加一些东西,直到我从 SO 那里得到答案:

        BreazEntities6 entities = new BreazEntities6();
        Document document = new Document { Id = 1 };
        entities.Documents.Attach(document);
        /*  The following line needs to be added in EF6, BUT not in EF4 */
        entities.Entry(document).Property(e => e.Active).IsModified = true;
        document.Active = false;
        entities.SaveChanges();
    
        return View();
    

您指出了具有 ObjectContext 的 EF 4.1 与具有 DbContext 的 EF 6(实际上是 4.1 及更高版本)在代码生成方面的差异。

ObjectContext API 中,一个单独的数据库字段,如 Active,将生成不少于此的代码:

/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Boolean Active
{
    get
    {
        return _Active;
    }
    set
    {
        OnActiveChanging(value);
        ReportPropertyChanging("Active");
        _Active = StructuralObject.SetValidValue(value);
        ReportPropertyChanged("Active");
        OnActiveChanged();
    }
}
private global::System.Boolean _Active;
partial void OnActiveChanging(global::System.Boolean value);
partial void OnActiveChanged();

意味着 EF 的更改跟踪器将立即收到每个 属性 更改的通知。

DbContext API 不仅改变了对开发人员更友好的 API,而且改变了更简单的 class 模型并转向了真实坚持无知。我相信一开始,仍然生成了这个 self-tracking 代码,但最后,它被放弃了,数据库字段将由一个简单生成的 auto-属性:

public bool Active { get; set; }

(毫无疑问,这样做也是为了在代码优先和数据库优先方法的基础上建立一个统一的代码库)。

不可避免地,这将更多责任放在更改跟踪器的肩上。它必须 检测 更改,而不仅仅是 注册 它们。为此,更改跟踪器方法 DetectChanges 被执行 "al the time".

返回您的代码。

在“旧”ObjectContextAPI...

document.Active = false;

... 会立即通知更改跟踪器 属性 已设置。 即使它已经是 false,也会在 SaveChanges 上发出 UPDATE

DbContext API 中,更改跟踪器永远不会将此检测为更改,因为 Active 已经具有布尔值的默认值 false没有任何变化

简而言之,以前是“你设置的就是你更新的”,现在变成了“你改的就是你更新的".

很好,现在怎么办?

您可能想回到原来的状态。

但是你呢?

试着稍微改变一下视角。假设您以代码为先,您会像第一个代码片段那样编写 属性 代码吗?你真的吗?首先,这是紧耦合,没有单一职责。在断开连接的情况下(即重新附加反序列化的实体),它可能会导致许多不必要的更新。

但是,即使您编写(或生成)类似的代码,EF 也不会侦听更改。您必须自己将具体化实体订阅到上下文中的一些更改传播代码。它不再是内置的,除非你 return 到 ObjectContext API (你不应该想要的)。

我会说:接受它。你输了一些,但你赢了很多(更多 SOLID 代码)。习惯 being in control of what is updated in the database and what isn't。您可能希望在实体的构造函数中将 Active 设置为 true 作为默认值,以便更改跟踪器确实注意到 false.

的更改

但我不认为开发人员忘记它是一个有效的论点。开发人员应该是灵活的。世界无时无刻不在变化,不随波逐流是不行的。如果他们记得包含 EF 特定语句 Attach(管理实体状态),为什么不包含管理 属性 状态的语句?