Кратко вставьте / обновите / удалите перечисляемые дочерние объекты с помощью Automapper - PullRequest
0 голосов
/ 22 мая 2018

Я работаю над приложением ASP.Net MVC, и у меня есть объект «Отчет», который имеет связанные перечислимые значения, такие как расписания и комментарии.Используя AutoMapper, было легко преобразовать объект отчета в модель представления и обратно, но у меня возникают проблемы при попытке сохранить объект отчета (сопоставленный существующему объекту из модели представления) обратно в базу данных.

Более конкретно, я не могу кратко обновить существующие сущности, вставить новые сущности и удалить старые сущности, используя automapper.Например, всякий раз, когда я сопоставляю расписания из модели представления с сущностью отчета, она удаляет существующие расписания, а затем создает новые (с увеличенными индексами).Это мой код:

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();
    }
}

Мой объект отчета имеет реляционный ключ (и навигационное свойство «Расписания»), поэтому все успешно отображается из моей модели представления в «исходный» отчет.Новые расписания имеют ScheduleID 0, поскольку они не были назначены, и они добавляются в базу данных с помощью автоинкремента, чего я и хочу.Существующие расписания сохраняют свой ScheduleID при сопоставлении с «исходным» объектом отчета, но затем получают увеличенные идентификаторы после вызова SaveChanges.

Насколько я понимаю, я присоединяю новые расписания к контексту независимо от того,Свойства идентификатора модели представления соответствуют первичному ключу в базе данных (в данном случае это составная часть ReportID и ScheduleID).Есть ли чистый способ, использующий какое-то ForMember (report => report.Schedules) выражение, которое заставляет Entity Framework понимать, что не следует уничтожать мои существующие сущности, если объект View Model может отображаться в существующий ключ?

Я ищу что-то похожее на приведенный ниже код, но, поскольку у меня будет много перечисляемых свойств, связанных с моими объектами отчета, я не хочу поддерживать эти разделы для каждого:

foreach (Schedule schedule in db.Schedules.Where(s => s.ReportID == this.ReportID))
{
    ScheduleViewModel svm = this.Schedules.FirstOrDefault(s => s.ScheduleID == schedule.ScheduleID);

    //Update Existing
    if (svm != null)
        db.Entry(schedule).CurrentValues.SetValues(svm);

    //Delete Missing
    else
        db.Entry(schedule).State = System.Data.Entity.EntityState.Deleted;
}

//Insert New
foreach(ScheduleViewModel svm in this.Schedules.Where(s => s.ScheduleID == 0))
{
    svm.ReportID = ReportID;
    Schedule schedule = new Schedule() {};
    db.Schedules.Add(schedule);
    db.Entry(schedule).CurrentValues.SetValues(svm);
}

1 Ответ

0 голосов
/ 22 мая 2018

Очевидно, что в настоящее время это невозможно с AutoMapper.Вместо сопоставления отдельных сущностей AutoMapper уничтожает существующую коллекцию сущностей и создает новую с такими же свойствами.Я уверен, что это хорошо работает для некоторых приложений, но с отслеживанием изменений в Entity Framework он сообщает базе данных об удалении существующих записей и вставке новых с новыми идентификаторами.К сожалению, кажется, что коллекции должны отображаться индивидуально / вручную, используя метод, который я разместил в моем первоначальном вопросе.Вместо того, чтобы повторять это для каждой коллекции, я написал общий обработчик, который будет сопоставлять коллекцию моделей с коллекцией сущностей с использованием указанного ключа - без разрушения сущностей:

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");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...