无法在实体框架5中检索原始值
本文关键字:检索 原始 框架 实体 | 更新日期: 2023-09-27 18:29:51
我正在编写一个asp.net mvc4应用程序,我正在使用实体框架5。我的每个实体都有类似EnteredBy
、EnteredOn
、LastModifiedBy
和LastModifiedOn
的字段。
我正在尝试使用SavingChanges
事件自动保存它们。下面的代码是从许多博客、SO回复等中整理出来的。
public partial class myEntities : DbContext
{
public myEntities()
{
var ctx = ((IObjectContextAdapter)this).ObjectContext;
ctx.SavingChanges += new EventHandler(context_SavingChanges);
}
private void context_SavingChanges(object sender, EventArgs e)
{
ChangeTracker.DetectChanges();
foreach (ObjectStateEntry entry in
((ObjectContext)sender).ObjectStateManager
.GetObjectStateEntries
(EntityState.Added | EntityState.Modified))
{
if (!entry.IsRelationship)
{
CurrentValueRecord entryValues = entry.CurrentValues;
if (entryValues.GetOrdinal("LastModifiedBy") > 0)
{
HttpContext currContext = HttpContext.Current;
string userName = "";
DateTime now = DateTime.Now;
if (currContext.User.Identity.IsAuthenticated)
{
if (currContext.Session["userId"] != null)
{
userName = (string)currContext.Session["userName"];
}
else
{
userName = currContext.User.Identity.Name;
}
}
entryValues.SetString(
entryValues.GetOrdinal("LastModifiedBy"), userName);
entryValues.SetDateTime(
entryValues.GetOrdinal("LastModifiedOn"), now);
if (entry.State == EntityState.Added)
{
entryValues.SetString(
entryValues.GetOrdinal("EnteredBy"), userName);
entryValues.SetDateTime(
entryValues.GetOrdinal("EnteredOn"), now);
}
else
{
string enteredBy =
entry.OriginalValues.GetString(entryValues.GetOrdinal("EnteredBy"));
DateTime enteredOn =
entry.OriginalValues.GetDateTime(entryValues.GetOrdinal("EnteredOn"));
entryValues.SetString(
entryValues.GetOrdinal("EnteredBy"),enteredBy);
entryValues.SetDateTime(
entryValues.GetOrdinal("EnteredOn"), enteredOn);
}
}
}
}
}
}
我的问题是entry.OriginalValues.GetString(entryValues.GetOrdinal("EnteredBy"))
和entry.OriginalValues.GetDateTime(entryValues.GetOrdinal("EnteredOn"))
不是返回原始值,而是返回当前值,即null
。我用实体中的其他字段进行了测试,它们返回了在html表单中输入的当前值
如何在此处获取原始值?
我认为问题可能是您正在使用模型绑定器提供的实例作为控制器方法的输入,因此EF对该实体及其原始状态一无所知。您的代码可能如下所示:
public Review Update(Review review)
{
_db.Entry(review).State = EntityState.Modified;
_db.SaveChanges();
return review;
}
在这种情况下,EF对正在保存的Review
实例一无所知。它信任您并将其设置为已修改,因此它将把它的所有属性保存到数据库中,但它不知道该实体的原始状态''值。
检查本教程中名为实体状态和附加和保存更改方法的部分。您还可以查看本文的第一部分,该部分显示EF如何不知道原始值,并将更新数据库中的所有属性。
由于EF需要了解原始属性,您可以首先从数据库加载实体,然后使用控制器中接收的值更新其属性。类似这样的东西:
public Review Update(Review review)
{
var reviewToSave = _db.Reviews.SingleOrDefault(r => r.Id == review.Id);
//Copy properties from entity received in controller to entity retrieved from the database
reviewToSave.Property1 = review.Property1;
reviewToSave.Property2 = review.Property2;
...
_db.SaveChanges();
return review;
}
这样做的优点是,只有修改后的属性才会在数据库中发送和更新,并且视图和视图模型不需要公开业务对象中的每个字段,只需要公开用户可以更新的字段。(为视图模型和模型''业务对象的不同类打开了大门)。明显的缺点是,您将对数据库造成额外的打击。
我在上面引用的教程中提到的另一个选项是,您可以以某种方式保存原始值(隐藏字段、会话等),并在保存时使用原始值将实体附加到未修改的数据库上下文。然后使用编辑的字段更新该实体。但是,除非您真的需要避免额外的数据库命中,否则我不建议使用这种方法。
希望能有所帮助!
我在尝试审核实体的Modified值日志时遇到了类似的问题。
事实证明,在回邮过程中,ModelBinder无法访问原始值,因此收到的Model缺少正确的信息。我通过使用这个函数解决了我的问题,该函数克隆当前值,重新锁定对象,然后重置当前值。
void SetCorrectOriginalValues(DbEntityEntry Modified)
{
var values = Modified.CurrentValues.Clone();
Modified.Reload();
Modified.CurrentValues.SetValues(values);
Modified.State = EntityState.Modified;
}
您可以通过更改跟踪器或上下文中的entry函数访问DbEntityEntry。