C# EF6 代码优先实体状态
Posted
技术标签:
【中文标题】C# EF6 代码优先实体状态【英文标题】:C# EF6 code-first EntityState 【发布时间】:2017-01-03 04:44:59 【问题描述】:我有一个使用 EF6 更新数据的 C# Web。并且在更新期间将触发 SQL Server 触发器。触发器应该通过
找到更新的列SELECT
@Columns_Updated = ISNULL(@Columns_Updated + ',', '') + name
FROM
syscolumns
WHERE
id = @idTable
AND CONVERT(VARBINARY, REVERSE(COLUMNS_UPDATED())) & POWER(CONVERT(BIGINT, 2), colorder - 1) > 0
我在下面的陈述中有一个问题
CrmClientContact t1 = dbdb.CrmClientContact.Where(x => x.rowId == 4).FirstOrDefault();
t1.updatedAt = DateTime.Now;
dbdb.Entry(t1).State = EntityState.Modified;
dbdb.SaveChanges();
CrmClientBetLimit t2 = dbdb.CrmClientBetLimit.Where(x => x.rowId == 1028).FirstOrDefault();
t2.updatedAt = DateTime.Now;
dbdb.Entry(t2).State = EntityState.Modified;
dbdb.SaveChanges();
CrmClientCLState t3 = dbdb.CrmClientCLState.Where(x => x.rowId==1).FirstOrDefault();
t3.updatedAt = DateTime.Now;
dbdb.Entry(t3).State = EntityState.Modified;
dbdb.SaveChanges();
这是我在 EF6 框架中更新记录的通常做法。
如图所示,我已经更新了这 3 个表中的字段 updatedAt
。
但是,触发器显示完全不同@Columns_Updated
表 1:clientId, createdAt, createdBy, name, rowId for t1(表中有 11 列)
表 2:betLimitValueSum、clientId、createdAt、createdBy、currId、updatedAt、updatedBy for t2(表中有 8 列)
表 3. t3 的 rowId(表中有 9 列)
我找不到他们返回这些列的原因。
注意:两个表都包含列createdAt, createdBy, updatedAt, updatedBy
为了解决这个问题,我尝试从代码中删除这些语句
dbdb.Entry(t1).State = EntityState.Modified;
dbdb.Entry(t2).State = EntityState.Modified;
dbdb.Entry(t3).State = EntityState.Modified;
因此,我认为问题在于在db.SaveChange()
之前附加EntityState
。
我想知道
为什么附加EntityState
会导致数据表中出现那些意外更新列?
什么时候应该附上EntityState.Modified
(我认为像上面的代码一样更新记录时这样做是合适的,但显然触发器显示它不是)?
-----------------更新----------------- 从代码中删除 EntityState.Modified 后。触发器仍然无法接收到正确的@UpdatedColumns,我尝试了下面的代码
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 7).FirstOrDefault();
t3.updatedAt = DateTime.Now;
t3.updatedBy = DateTime.Now.ToString();
t3.mobile = DateTime.Now.ToString();
t3.name = DateTime.Now.ToString();
dbdb.SaveChanges();
但是,在触发器中,它返回 createdAt,rowId 作为更新的列。请注意,某些表格可以正常工作。
而且这种行为对我来说是出乎意料的。举例
CrmClientContact t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedAt = DateTime.Now;
t3.email = DateTime.Now.ToString();
dbdb.SaveChanges(); // correct
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedAt = DateTime.Now.AddDays(1);
t3.email = DateTime.Now.ToString() + 1;
dbdb.SaveChanges(); //correct
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedAt = DateTime.Now.AddDays(2);
t3.email = DateTime.Now.ToString() + 2;
t3.mobile = DateTime.Now.ToString() + 2;
t3.name = DateTime.Now.ToString() + 2;
dbdb.SaveChanges(); // correct
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedAt = DateTime.Now;
t3.updatedBy = DateTime.Now.ToString();
dbdb.SaveChanges(); // correct
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedAt = DateTime.Now.AddDays(1);
t3.updatedBy = DateTime.Now.ToString()+1;
dbdb.SaveChanges(); // correct
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedAt = DateTime.Now.AddDays(2);
t3.updatedBy = DateTime.Now.ToString() + 2;
dbdb.SaveChanges(); // correct
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedBy =3+ DateTime.Now.ToString();
t3.email =1+ DateTime.Now.ToString();
dbdb.SaveChanges();//null, incorrect
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedBy =2+ DateTime.Now.ToString();
t3.email =1+ DateTime.Now.ToString();
dbdb.SaveChanges();//updatedBy, incorrect (email is missing)
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedBy = 22 + DateTime.Now.ToString();
t3.email = 11 + DateTime.Now.ToString();
dbdb.SaveChanges();//null, incorrect
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedBy = 222 + DateTime.Now.ToString();
t3.email = 111 + DateTime.Now.ToString();
dbdb.SaveChanges();//null, incorrect
t3 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t3.updatedBy = 22 + DateTime.Now.ToString();
t3.email = 11 + DateTime.Now.ToString();
dbdb.SaveChanges();//null, incorrect
CrmClientContact t4 = dbdb.CrmClientContact.Where(x => x.rowId == 5).FirstOrDefault();
t4.updatedBy = 2 + DateTime.Now.ToString();
t4.email = 1 + DateTime.Now.ToString();
dbdb.SaveChanges();//null, incorrect
如上所示,我完全不明白上述结果集是如何产生的(它们是确定性的,可重复的)
--------------最终更新 -------------- 使用 SQL Profiler 后,我发现 EF6 的 SQL 查询只是一个与文档匹配的普通更新语句。并且在管理工作室执行相同的SQL时可以重现相同的结果(在触发器中找不到UPDATED COLUMN)。
结果,我认为SQL Server Update Trigger, Get Only modified fields 只能用于以前版本的SQL Server。
至少我的数据库 (SQLSERVER 2014 (120)) 无法应用标记的答案来查找更新的列。
最后,我应用了另一个对已删除和插入的表进行透视并找到不同的方法,没有理由再失败,除非它需要表中未更改的列(幸运的是,我所有的表都有应该未更改的主键) .
唯一的问题是维护每个表修改的触发器。
谢谢。
【问题讨论】:
【参考方案1】:来自MSDN:
当您将状态更改为已修改时,实体的所有属性将被标记为已修改,并且所有属性值将在调用 SaveChanges 时发送到数据库。
当您从数据库中检索实体对象时,EF 会为您跟踪实体对象的状态。这意味着更改任何属性值并使用 SaveChanges
保存实体也会保留您的更改,并且您无需将实体状态显式标记为 EntityState.Modified
。
希望这会有所帮助。
附:如果您有审计需求,可以查看EntityFramework-Plus Audit。我建议,因为我们在工作中使用它,我与“EntityFramework-Plus”没有任何关系。
【讨论】:
奇怪的是附加 EntityState 并没有更新所有列。我认为这是因为 SQL 语句不适用于查找更新的列。但是,如果我直接在管理工作室中执行 SQL,我可以正确获取更新的列。这是我真正怀疑的主要问题。事实上,我只想使用 EF6 执行“update t1 set updatedAt=GETDATE() where rowid=4”,这样我就可以在触发器中以 @UpdatedColumn 的形式获取 updateAt。不幸的是,上面的 EF6 更新不起作用(它返回奇怪的列!) @SKLTFZ 在这种情况下,我建议记录 sql 查询。您可以查看msdn.microsoft.com/en-us/library/…。或者,您可以运行 SQL Server Profiler(可从 SSMS 获得)来检查正在对您的数据库执行哪些查询。以上是关于C# EF6 代码优先实体状态的主要内容,如果未能解决你的问题,请参考以下文章