#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
приведет к ОБНОВЛЕНИЮ всех свойств — независимо от того, действительно ли они изменились или нет.)