0

I'm using EF 4.1 Code First. I have an entity defined with a property like this:

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

I've configured this foreign key using fluent style like so:

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

I have an MVC form handler with some code in it that looks like this:

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

In this case, we're updating an existing Publication entity, so we're going through the else path. When the _unitOfWork.Commit() fires, an UPDATE is sent to the database that I can see in SQL Profiler and Intellitrace, but it does NOT include the MailoutTemplateID in the update.

What's the trick to get it to actually update the Template?

Repository Code:

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

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

UnitOfWork Code:

public void Commit()
{
    _dbContext.SaveChanges();
}
ssmith
  • 8,092
  • 6
  • 52
  • 93
  • If it matters, the MailoutTemplateID is not set at the start of this. – ssmith Oct 11 '11 at 15:40
  • Try to move the line `publication.Template = ...` to the bottom of the method right before `...Commit`. Change detection recognizes then hopefully that the template changed and the ID must be sent to the DB. Setting the state to `Modified` has no effect on the `MailoutTemplateID` column because it's not a scalar property in your model. – Slauma Oct 11 '11 at 16:41

2 Answers2

1

depends on your repository code. :) If you were setting publication.Template while Publication was being tracked by the context, I would expect it to work. When you are disconnected and then attach (with the scenario that you have a navigation property but no explicit FK property) I'm guessing the context just doesn't have enough info to work out the details when SaveChanges is called. I'd do some experiments. 1) do an integration test where you query the pub and keep it attached to the context, then add the template, then save. 2) stick a MailOutTemplateId property on the Publicaction class and see if it works. Not suggesting #2 as a solution, just as a way of groking the behavior. I"m tempted to do this experiment, but got some other work I need to do. ;)

Julie Lerman
  • 4,602
  • 2
  • 22
  • 21
  • I updated the question with the Repo and UoW code. Is the issue that I am not performing a Get() on Publication, but am simply mapping it from the view model? All of the other fields update correctly - it's only this FK ID that is not. – ssmith Oct 11 '11 at 16:04
0

I found a way to make it work. The reason why I didn't initially want to have to do a Get() (aside from the extra DB hit) was that then I couldn't do this bit of AutoMapper magic to get the values:

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

However, I found another way to do the same thing that doesn't use a return value, so I updated my method like so and this works:

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

I'm injecting an IMappingEngine now into the class, and have wired it up via StructureMap like so:

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

For more on this, check out Jimmy's AutoMapper and IOC post.

ssmith
  • 8,092
  • 6
  • 52
  • 93
  • Hmm, why do you have to wait 2 days to mark your own answer as the answer when you know, immediately, that it solves your problem? Like I'm really going to remember to come back to this in 2 days? How many other potential answerers are going to waste their time on this over the next 2 days? – ssmith Oct 11 '11 at 17:07
  • You can in this case remove your `Update` method (the else block) completely. `_mappingEngine.Map...` will set properties of the loaded (=attached) `publication`. When you call `SaveChanges` an UPDATE statement will be sent to the DB which contains only the properties which actually have changed. (Setting the state to `Modified` will cause an UPDATE on *all* properties - no matter if they really changed or not.) – Slauma Oct 11 '11 at 17:44