EF& MVC:如何避免将继承的值传递给视图

时间:2014-04-23 08:25:45

标签: c# entity-framework asp.net-mvc-4

我的模型中有很多实体继承自名为BaseEntity的抽象类。 然后有一个“编辑”控制器,它接收一个Person对象作为参数并保存更改。在“编辑”控件的视图中,用户显然可以编辑实体的某些字段。该视图仅包含PersonId作为隐藏字段,Name作为文本框。

我的实体看起来像这样:

    public abstract class BaseEntity
    {
        public DateTime DateCreated { get; set; }
        public DateTime DateLastModified { get; set; }
        public int? UserIdLastModified { get; set; }
    }

    public class Person : BaseEntity
    {
        int PersonId { get; set; }
        string Name { get; set; }
    }

这是控制器:

    [HttpPost]
    public ActionResult Edit(Person person, FormCollection collection)
    {
        /* ... do something with the FormCollection */
        db.Entry(person).State = EntityState.Modified;
        db.SaveChanges();
    }

我还覆盖了db.SaveChanges()方法,因此它会自动设置DateLastModified字段。但由于DateCreated将为DateTime.MinValue,因此SaveChanges()会抛出异常。

为了隐私和简单,我不想将BaseEntity的字段作为隐藏字段添加到View中。我知道我可以从Controller中的数据库加载实体,只更新Name,但它并不是我最好的解决方案,因为我必须在很多“编辑”控制器中这样做。每次模型中的某些变化我都必须更改控制器和视图。

在保存之前将DateCreated恢复到控制器的好方法是什么?

编辑: 谢谢你的答案。但是,这是我的SaveChanges方法。回答一些评论并进一步解释:问题是,如果我只设置DateLastModified,如下所述,base.SaveChanges()将尝试用'1/1/0001覆盖DateCreated凌晨12:00:00'(因为它从未设置过)。这正确地引发了异常。

我现在要做的是添加一些逻辑以在SaveChanges方法中获取BaseEntity的先前值。显然我需要在某处进行另一个数据库调用,这样我只在一个地方而不是每个控制器都有逻辑。如果这种方法有问题,请告诉我。

    public override int SaveChanges()
    {
        ObjectContext context = ((IObjectContextAdapter)this).ObjectContext;

        //Find all Entities that are Added/Modified that inherit from my EntityBase
        var objectStateEntries =
            (from e in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
             where e.IsRelationship == false && e.Entity != null && typeof(BaseEntity).IsAssignableFrom(e.Entity.GetType())
             select e).ToList();

        foreach (var entry in objectStateEntries)
        {
            var entityBase = (BaseEntity)entry.Entity;
            if (entry.State == EntityState.Added)
            {
                entityBase.DateCreated = DateTime.UtcNow;
            }                   
            entityBase.DateModified = DateTime.UtcNow;

            //new code for getting the previous value
            if (entityBase.DateCreated == DateTime.MinValue)
            {
                //this is not working. just to show you my idea
                var currentDBItem = this.Entry(entry.Entity).Entity as BaseEntity; //get previous value from database
                entityBase.DateCreated = currentDBItem.DateCreated;
            }
        }
        return base.SaveChanges();
    }

3 个答案:

答案 0 :(得分:1)

当您需要支持 ViewModels 而不是视图中的实体时,这听起来像是一个经典案例。

但是,假设您不打算更改体系结构,则只需更改DateCreated字段,除非它处于“已添加”状态,而DateLastModified仅限于“已修改”状态州。类似的东西:

public override void SaveChanges()
{
    foreach (var entry in this.ChangeTracker.Entries<BaseEntity>())
    {
        switch (entry.State)
        {
            case EntityState.Added:
                entry.Entity.DateCreated = DateTime.Now;
                break;

            case EntityState.Modified:
                entry.Entity.DateLastModified = DateTime.Now;
                break;
        }
    }

    base.SaveChanges();
}

此外,您最好将DateLastModified属性设为可以为DateTime?,因为它在首次更新后只会有一个值。

根据您的更新:

试试这个:

if (entry.State == EntityState.Added)
    entityBase.DateCreated = DateTime.UtcNow;
else
    entry.Property(x => x.DateCreated).IsModified = false;

答案 1 :(得分:0)

我认为您最好的选择实际上是根据您在隐藏字段中存储的ID从数据库中检索原始实体,并仅更新可能已更改的值。基本上你提出的解决方案。

我知道这看起来很乏味,但这是一种常见做法。

顺便说一下,我还建议调查view models

答案 2 :(得分:0)

没有&#34; easy&#34;解决方案(例如将DateCreated添加为隐藏字段)。 最好再次从数据库加载数据,或者您可以使用视图模型并编写自己的ModelBinder。