您能否告诉我为什么Entity Framework 6需要使用“IsModified”代码进行附加更新,否则我的代码将“Silent Fail”vs Entity Framework 4?换句话说,在实体框架4中,我使用附加工具进行更新。但是在EF6中,如果我做类似的话,db将不会更新并且不会抛出异常(静默失败)。如果我把“IsModified”行放在我的代码中就可以了,但是这是不可接受的,因为开发人员可以保留“IsModified”代码,更新将失败,没有人会知道。
在db中设置的以下条件下,EF6中会发生/不会发生此问题: 1.如果active设置为default 1且AllowNulls = false,则更新失败 2.如果active未设置为默认值且AllowNulls = false,则更新失败 3.如果active未设置为默认值且AllowNulls = true,则更新有效 4.如果active设置为default 1且AllowNulls = true,则更新工作
这类似于:EntityFramework not saving null and false value但不完全相同。我将引导您解决问题:
1) 如果你有Visual Studio 2010,你可以跟随,否则,你可以相信EF4按照描述的方式工作。
在Visual Studio 2010中,使用.NET Framework 4的新项目ASP.NET MVC 2 Web应用程序,并将其命名为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。
5)在Home Index Controller中(如有必要,添加此控制器)添加并运行此代码(类似)并检查数据库是否工作!(更新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添加到Document表中。
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();
}
这不起作用。我将不得不执行以下操作,这是不可接受的,因为新开发人员可以排除以下内容并且不知道代码不起作用。有什么东西,我可以全局添加到解决方案中,不让开发人员添加以下内容吗?我将研究并尝试自己添加一些内容,直到我从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();
答案 0 :(得分:1)
您指出EF 4.1与ObjectContext
和EF 6(实际上是4.1及更高版本)与DbContext
之间代码生成的差异。
在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的改变,也是对更简单的类模型的改变,以及对真正持久性无知的转变。我相信在开始时,仍会生成此自我跟踪代码,但最终,这被放弃了,数据库字段将由简单生成的自动属性表示:
public bool Active { get; set; }
(毫无疑问,这也是为了在代码优先和数据库优先方法的基础上建立一个统一的代码库。)
不可避免地,这使更改跟踪器的肩膀承担更多责任。它必须检测更改,而不仅仅是注册它们。因此,更改跟踪器方法DetectChanges
将执行"al the time"。
返回您的代码。
在“旧”ObjectContext
API ...
document.Active = false;
...会立即通知变更跟踪器该属性已设置。 即使它已经是假的,UPDATE
上也会发出SaveChanges
。
在DbContext
API中,更改跟踪器永远不会将此检测为更改,因为Active
已经具有布尔值的默认值false
。没有更改。
简而言之,它曾经是“你设置的是你更新的内容”,它变成了“你改变的是你更新的内容”。
你可能想回到你拥有的东西。
但你呢?
尝试稍微改变一下。假设您首先使用代码工作,您是否会像第一个代码片段一样编写属性代码?你真的吗?首先,这是紧密耦合,没有单一责任。在断开连接的场景中(即重新附加反序列化的实体),可能会导致许多不必要的更新。
但是,即使您要编写(或生成)类似的代码,EF也不会收听更改。您必须自己将物化实体订阅到上下文中的某些更改传播代码。它不再内置,除非您返回ObjectContext
API(您不应该这样做)。
我会说:和它一起生活。你失去了一些,但你赢得了很多(更多的SOLID代码)。习惯于being in control of what is updated in the database and what isn't。您可能希望将Active
设置为true作为实体构造函数中的默认值,因此更改跟踪器会注意到对false
的更改。
但我不认为开发人员忘记这是一个有效的论点。开发人员应该是灵活的。世界一直在变化,而不是随之而来的不是一种选择。如果他们记得包含EF特定语句Attach
(管理实体状态),为什么不管理属性状态的语句?