1

我正在编写一个 asp.net mvc4 应用程序,我正在使用实体框架 5。我的每个实体都有像EnteredByEnteredOn和.LastModifiedByLastModifiedOn

我正在尝试使用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 表单中输入的当前值。

我如何在这里获得原始值?

4

2 回答 2

5

我认为问题可能是您使用模型绑定器提供的实例作为控制器方法的输入,因此 EF 对该实体及其原始状态一无所知。您的代码可能如下所示:

public Review Update(Review review)
{
    _db.Entry(review).State = EntityState.Modified;
    _db.SaveChanges();
    return review;
}

Review在这种情况下,EF 对正在保存的实例一无所知。它信任您并将其设置为已修改,因此它将其所有属性保存到数据库中,但它不知道该实体的原始状态\值。

检查名为 Entity States 的部分以及本教程的 Attach 和 SaveChanges 方法。您还可以查看本文的第一部分,该部分显示 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;
}

这样做的好处是,只有修改过的属性才会在数据库中发送和更新,并且您的视图和视图模型不需要公开业务对象中的每个字段,只需要那些可以由用户更新的字段。(为视图模型和模型\业务对象打开不同类的大门)。明显的缺点是您将对数据库造成额外的打击。

我在上面引用的教程中提到的另一个选项是让您以某种方式保存原始值(隐藏字段、会话等),并在保存时使用原始值将实体附加到数据库上下文中作为未修改的。然后使用已编辑的字段更新该实体。但是,除非您确实需要避免额外的数据库命中,否则我不会推荐这种方法。

希望有帮助!

于 2013-09-02T08:24:26.027 回答
3

在尝试审核实体的修改值时,我遇到了类似的问题。

事实证明,在回发期间,ModelBinder 无法访问原始值,因此收到的模型缺少正确的信息。我通过使用克隆当前值、重新加载对象、然后重置当前值的函数来解决我的问题。

    void SetCorrectOriginalValues(DbEntityEntry Modified)
    {
        var values = Modified.CurrentValues.Clone();
        Modified.Reload();
        Modified.CurrentValues.SetValues(values);
        Modified.State = EntityState.Modified;
    }

您可以通过更改跟踪器或上下文中的条目函数访问 DbEntityEntry。

于 2014-10-01T18:44:18.487 回答