Первое обновление кода Entity Framework не обновляет внешний ключ

#entity-framework-4.1 #ef-code-first

#entity-framework-4.1 #ef-code-first

Вопрос:

Сначала я использую код EF 4.1. У меня есть объект, определенный с таким свойством, как это:

 public class Publication 
{
    // other stuff
    public virtual MailoutTemplate Template { get; set; }
}
  

Я настроил этот внешний ключ, используя свободный стиль, например:

     modelBuilder.Entity<Publication>()
        .HasOptional(p => p.Template)
        .WithMany()
        .Map(p => p.MapKey("MailoutTemplateID"));
  

У меня есть обработчик формы MVC с некоторым кодом, который выглядит следующим образом:

 public void Handle(PublicationEditViewModel publicationEditViewModel)
        {
            Publication publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
            publication.Template = _mailoutTemplateRepository.Get(publicationEditViewModel.Template.Id);
            if (publication.Id == 0)
            {
                _publicationRepository.Add(publication);
            }
            else
            {
                _publicationRepository.Update(publication);
            }
            _unitOfWork.Commit();
        }
  

В этом случае мы обновляем существующий объект публикации, поэтому мы идем по пути else. Когда срабатывает _unitOfWork.Commit(), в базу данных отправляется ОБНОВЛЕНИЕ, которое я вижу в SQL Profiler и Intellitrace, но оно НЕ включает MailoutTemplateID в обновлении.

В чем хитрость, чтобы заставить его на самом деле обновить шаблон?

Код репозитория:

 public virtual void Update(TEntity entity)
{
    _dataContext.Entry(entity).State = EntityState.Modified;
}

public virtual TEntity Get(int id)
{
    return _dbSet.Find(id);
}
  

Код UnitOfWork:

 public void Commit()
{
    _dbContext.SaveChanges();
}
  

Комментарии:

1. Если это имеет значение, идентификатор MailoutTemplateID не установлен в начале этого.

2. Попробуйте переместить строку publication.Template = ... в конец метода прямо перед ...Commit этим. Надеюсь, обнаружение изменений распознает, что шаблон изменился, и идентификатор должен быть отправлен в БД. Установка состояния на Modified значение не влияет на MailoutTemplateID столбец, потому что это не скалярное свойство в вашей модели.

Ответ №1:

зависит от вашего кода репозитория. 🙂 Если бы вы устанавливали публикацию.Шаблон в то время как публикация отслеживалась контекстом, я ожидал бы, что это сработает. Когда вы отключаетесь, а затем подключаетесь (в сценарии, в котором у вас есть свойство навигации, но нет явного свойства FK) Я предполагаю, что в контексте просто недостаточно информации для уточнения деталей при вызове SaveChanges. Я бы провел несколько экспериментов. 1) выполните интеграционный тест, в котором вы запрашиваете pub и сохраняете его привязанным к контексту, затем добавляете шаблон, а затем сохраняете. 2) вставьте свойство MailOutTemplateId в класс Publicaction и посмотрите, работает ли оно. Не предлагая № 2 в качестве решения, а просто как способ изменить поведение. Меня так и подмывает провести этот эксперимент, но у меня есть кое-какая другая работа, которую мне нужно сделать. 😉

Комментарии:

1. Я обновил вопрос с помощью кода репозитория и UoW. Проблема в том, что я не выполняю Get () при публикации, а просто сопоставляю его с моделью представления? Все остальные поля обновляются правильно — нет только этого FK ID.

Ответ №2:

Я нашел способ заставить его работать. Причина, по которой я изначально не хотел выполнять Get() (помимо дополнительного попадания в БД), заключалась в том, что тогда я не мог выполнить эту часть магии автоматического отображения, чтобы получить значения:

 Publication publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
  

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

     public void Handle(PublicationEditViewModel publicationEditViewModel)
        {
            Publication publication = _publicationRepository.Get(publicationEditViewModel.Id);
            _mappingEngine.Map(publicationEditViewModel, publication);
//            publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
            publication.Template = _mailoutTemplateRepository.Get(publicationEditViewModel.Template.Id);
            if (publication.Id == 0)
            {
                _publicationRepository.Add(publication);
            }
            else
            {
                _publicationRepository.Update(publication);
            }
            _unitOfWork.Commit();
        }
  

Сейчас я внедряю IMappingEngine в класс и подключил его через StructureMap следующим образом:

     For<IMappingEngine>().Use(() => Mapper.Engine);
  

Подробнее об этом читайте в публикации Джимми AutoMapper и IOC.

Комментарии:

1. Хм, почему вам нужно ждать 2 дня, чтобы пометить свой собственный ответ как ответ, когда вы сразу знаете, что он решает вашу проблему? Как будто я действительно не забуду вернуться к этому через 2 дня? Сколько других потенциальных ответчиков будут тратить свое время на это в течение следующих 2 дней?

2. В этом случае вы можете полностью удалить свой Update метод (блок else). _mappingEngine.Map... установит свойства загруженного (= прикрепленного) publication . При вызове SaveChanges инструкции UPDATE в базу данных будет отправлено сообщение, содержащее только те свойства, которые действительно были изменены. (Установка состояния на Modified приведет к ОБНОВЛЕНИЮ всех свойств — независимо от того, действительно ли они изменились или нет.)