我正在开发一个ASP.Net MVC应用程序,我有一个“报告”对象,它有相关的枚举,如日程安排和评论。使用AutoMapper,很容易从报表实体转换为View Model并返回,但是当我尝试将Report对象(从视图模型映射到现有实体)保存回数据库时,我遇到了问题。
更具体地说,我似乎无法使用automapper简洁地更新现有实体,插入新实体和删除旧实体。例如,每当我将视图模型中的日程安排映射到报表实体时,它都会删除现有的日程表,然后创建新的日程表(具有递增的索引)。这是我的代码:
Bot2
我的报告对象有一个关系密钥(以及“计划”导航属性),因此所有内容都从我的视图模型成功映射到“原始”报告。新计划的ScheduleID为0,因为它们尚未分配,并且使用自动增量将它们添加到数据库中,这就是我想要的。现有的计划在映射到“原始”报告对象时保持其ScheduleID,但是一旦调用了SaveChanges,它就会收到递增的ID。
据我所知,无论视图模型ID属性是否与数据库中的主键匹配(在这种情况下,它是ReportID和ScheduleID的组合),我都会在上下文中附加新的计划。是否有一种干净的方式,使用某种ForMember(report => report.Schedules)表达式,使得Entity Framework能够理解,如果View Model对象可以映射到现有Key,则不会破坏我现有的实体?
我正在寻找与下面的代码类似的功能,但由于我的报表对象附加了许多可枚举的属性,我不想为每个部分维护这些部分:
public class ScheduleViewModel
{
public int ScheduleID { get; set; }
public int ReportID { get; set; }
public int Month { get; set; }
public int Day { get; set; }
public string Description { get; set; }
}
public class ReportViewModel
{
public int ReportID { get; set; }
public List<ScheduleViewModel> Schedules { get; set; }
public void Save()
{
dbContext db = new dbContext();
Report original = db.Reports.SingleOrDefault(o => o.ReportID == ReportID);
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ReportViewModel, Report>();
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
mapper.Map(this, original);
db.SaveChanges();
}
}
答案 0 :(得分:-1)
显然,AutoMapper目前无法实现这一目标。 AutoMapper不是映射单个实体,而是销毁现有的实体集合并创建具有相同属性的新集合。我确信它适用于某些应用程序,但是使用Entity Framework的更改跟踪,它告诉数据库删除现有记录并使用新ID插入新记录。不幸的是,似乎必须使用我在原始问题中发布的方法单独/手动映射集合。不过,我没有为每个集合重复这一点,而是编写了一个通用处理程序,它将使用指定的键将模型集合映射到实体集合 - 而不会破坏实体:
public void UpdateEntitySet<T>(IEnumerable<object> models, IEnumerable<T> entities, string key) where T : class
{
Dictionary<object, T> entityDictionary = new Dictionary<object, T>();
foreach(T entity in entities)
{
var entityKey = entity.GetType().GetProperty(key).GetValue(entity);
entityDictionary.Add(entityKey, entity);
}
if (models != null)
{
foreach (object model in models)
{
var modelKey = model.GetType().GetProperty(key).GetValue(model);
var existingEntity = entityDictionary.SingleOrDefault(d => Object.Equals(d.Key, modelKey)).Value;
if (existingEntity == null)
{
var newEntity = db.Set<T>().Create();
db.Set<T>().Add(newEntity);
db.Entry(newEntity).CurrentValues.SetValues(model);
}
else
{
db.Entry(existingEntity).CurrentValues.SetValues(model);
entityDictionary.Remove(entityDictionary.Single(d => Object.Equals(d.Key, modelKey)).Key);
}
}
}
for (int i = 0; i < entityDictionary.Count; i++)
db.Entry(entityDictionary.ElementAt(i).Value).State = System.Data.Entity.EntityState.Deleted;
}
如果您有复合键,则必须稍微修改一下代码,否则您可以忽略AutoMapper的映射,然后使用上面的方法:
IEnumerable<Schedule> ScheduleEntities = db.Set<Schedule>().Where(s => s.ReportID == ReportID);
UpdateEntitySet<Schedule>(ScheduleViewModels, ScheduleEntities, "ScheduleID");