我的模型中有很多实体继承自名为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();
}
答案 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。