1

Update:

This is now driving me crazy!

After much Googling etc. I am really no closer to a solution.....

However I have found one thing that is puzzling me even more - the "States" of the entities just before the m_dbContext.SaveChanges() call. (see below for full repository code)

var updateInfoState = m_dc.Entry(oldPage.UpdateInfo).State; // State is 'Modified'
var oldPageState = m_dc.Entry(oldPage).State;               // State is 'Detached' 

this.m_dc.SaveChanges();

Why is "oldPage" detached?

Getting quite desperate now!! ;)

Original:

I appear to be having a problem with EF Code-First updating related tables correctly.

In this simplified example, the 'UpdateInfo' table IS being updated OK with the new DateTime .... but the 'Pages' table is not being updated with the new 'Name' value.

I am seeding code-first POCOs via DropCreateDatabaseAlways / override Seed ... and EF is creating the test tables correctly - so at this point it seems to know what it is doing....

I am sure this is something simple/obvious I am missing!

All help very much appreciated!

My Class definitions:

public class Page
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual UpdateInfo UpdateInfo { get; set; }  // virtual For Lazy loading
}

[Table("UpdateInfo")]                                   // much better than EF's choice of UpdateInfoes! 
public class UpdateInfo
{
    public int Id { get; set; }
    public DateTime DateUpdated { get; set; }
}

public class DomainContext : DbContext
{
    public DbSet<Page> Pages { get; set; }
    public DbSet<UpdateInfo> UpdateInfo { get; set; }
}

Tables created by Code-First

Pages Table
===========

    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](max) NULL,
    [UpdateInfo_Id] [int] NULL,

UpdateInfo Table
================

    [Id] [int] IDENTITY(1,1) NOT NULL,
    [DateUpdated] [datetime] NOT NULL,

My Repository code:

public Page Get(int id)
{
    Page page = m_dbContext.Pages.Single(p => p.Id == id);
    return page;
}

public void Update(PagePostModel model)
{
    Page oldPage = Get(model.PageModel.Id);                         // on oldPage Name = "Hello", DateUpdated = "Last Year"

    Page newPage = Mapper.Map<PageModel, Page>(model.PageModel);    // on newPage Name = "Goodbye"  (AutoMapper)
    newPage.UpdateInfo = oldPage.UpdateInfo;                        // take a copy of the old UpdateInfo since its not contained in the model

    newPage.UpdateInfo.DateUpdated = DateTime.UtcNow;               // update to now

    oldPage = newPage;                                              // copy the updated page we grabbed from dbContext above (NB Everything looks great here..oldPage is as expected)

    m_dbContext.SaveChanges();                                      // update - only the 'UpdateInfo' table is being updated - No change to 'Pages' table :(((
}
JcMaltaDev
  • 1,344
  • 1
  • 9
  • 17

1 Answers1

2

As you know, there is a change tracker api in Entity Framework.

To track the changes of your entities you retrieved from the database, DbContext uses its reference value.

Your "Update" function above inserts newPage into oldPage. So, DbContext never knows oldPage is a newPage. So, it is "detached".

However, for UpdateInfo, it is copy of reference in oldPage, so DbContext can track change of that. So, it is "modified".

To solve this problem, how about using the code below?

Page newPage = Mapper.Map<PageModel, Page>(model.PageModel);
oldPage.UpdateInfo = newPage.UpdateInfo; 
oldPage.UpdateInfo.DateUpdated = DateTime.UtcNow;
m_dbContext.SaveChanges();

Update

Then, use Attach & Detach methods.

Those methods help you attach and detach entities from DbContext.

Page newPage = Mapper.Map<PageModel, Page>(model.PageModel);
// if you attach first, there will be an exception, 
// because there is two entities having same id.
m_dbContext.Entry(oldPage).State = EntityState.Detached; 
m_dbContext.Pages.Attach(newPage);
// if you don't set IsModified = true,
// DbContext cannot know it is changed.
m_dbContext.Entry(newPage).State = EntityState.Modified;
m_dbContext.SaveChanges();
DevExcite
  • 399
  • 4
  • 6
  • Many thanks for your prompt reply - I have tried variations of that theme ... but how do I get the contents of 'newPage' into 'oldpage' without an assignment? Your very welcome example code is only updating the 'DateUpdated' value - NOT the changes made by the user via the passed model. – JcMaltaDev May 11 '12 at 06:53
  • Thanks! .. it is now working! For your information the "Detach" method has been deprecated ref http://stackoverflow.com/questions/4168073/entity-framework-code-first-no-detach-method-on-dbcontext, so I had to use m_dc.Entry(oldPage).State = EntityState.Detached; instead. Again - thanks for your help .. it was very much appreciated! – JcMaltaDev May 11 '12 at 11:05
  • Thanks for your information. I didn't know there is no Detach in DbContext. I updated the answer – DevExcite May 12 '12 at 00:38