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

本文关键字:继承 值传 视图 何避免 amp MVC EF | 更新日期: 2023-09-27 17:59:47

我的模型中有很多实体,它们继承自一个名为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();
    }

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

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

在保存DateCreated之前,有什么好方法可以将其返回到控制器?

编辑:谢谢你的回答。不过,这是我的SaveChanges方法。为了回答一些评论并进一步解释:问题是,如果我只像下面描述的那样设置DateLastModified,那么基数。SaveChanges()将尝试用"1/1/0001 12:00:00 AM"覆盖DateCreated(因为从未设置过)。这正确地抛出了异常。

我现在想做的是添加一些逻辑,以便在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();
    }

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

当您需要在视图中支持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属性设置为可为null的DateTime?,因为它只有在第一次更新后才有值。

根据您的更新:

试试这个:

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

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

我知道这看起来很无聊,但这是一种常见的做法。

顺便说一句,我还建议研究视图模型。

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