locked
EF 4.1/MVC3 updating a related record anyways adds and new record rather than updating it RRS feed

  • Question

  • I am converting a ASP.net application to MVC3/EF4.1, and its been a challenge so far.

    I have a simple database structure Player (1..1) => Person (0..many) => Phone. The updates for the Player and person work as I would expect, but when I try to update the Phone table EF clears the FK (null) and creates a new record. Everything appears to the linked correctly to the new record, but the why didn't the original record just get updated?

     

    [HttpPost]
      public ActionResult Edit(CPlayer cplayer)
      {
       if (ModelState.IsValid)
       {
    				EntityState es;
    
    				using ( MPAEntities db2 = new MPAEntities())
    				{
    					es = db2.Entry(cplayer).State;
    					es = db2.Entry(cplayer.Person).State;
    
    //				 var upd = db2.Players.Include( i=>i.Person ).Include(i=>i.Person.Phones).Where( i=>i.ID == cplayer.ID).Single();
    //					db2.Configuration.AutoDetectChangesEnabled = false;
    					db2.Players.Attach( cplayer );
    
    					if( TryUpdateModel( cplayer ))
    					{
    						UpdateModel(cplayer);
    						foreach( CPhone phn in cplayer.Person.Phones )
    						{
    							es = db2.Entry(phn).State;
    							//if( phn.ID != 0 )
    							// db2.Entry(phn).State = EntityState.Modified;
    						}
    
    						db2.Entry(cplayer).State = EntityState.Modified;
    						db2.Entry(cplayer.Person).State = EntityState.Modified;
    
    						db2.SaveChanges();
    						return RedirectToAction("Index");
    					}
    				}
    			}
    				return View(cplayer);
    

    The returned CPlayer entity has all the fields populated as expected, but the SaveChanges always creates a new Phone record and leaves the old one.

     

    The FK Field is also populated correctly, so the entire entity is populated exactly like the database record that it is supposed to update, but it still adds a new record and nulls the FK field of the record I am trying to update.

    I have read a hundred different tutorials and examples and my mind is just a blur. It seems that updating a table with EF is much more error prone than just writting the SQL, but I am determined to get a handle on this so any help is appreciated.


    Scott

    After playing around with this I changed the edit routine to:

        [HttpPost]
        public ActionResult Edit(CPlayer cplayer)
        {
          if (ModelState.IsValid)
          {
    				EntityState es;
    
    				using ( MPAEntities db2 = new MPAEntities())
    				{
    				  var upd = db2.Players.Include( i=>i.Person ).Include(i=>i.Person.Phones).Where( i=>i.ID == cplayer.ID).Single();
    					//
    					//	Update Phone records
    					//
    					foreach( CPhone phn in cplayer.Person.Phones )
    					{
    						foreach( CPhone phn2 in upd.Person.Phones )
    						{
    							if( phn.ID == phn2.ID )
    							{
    								phn2.nPhoneType		= phn.nPhoneType;
    								phn2.PhoneNumber	= phn.PhoneNumber;
    								goto NextPhone;
    							}
    						}
    						upd.Person.Phones.Add( phn );
    NextPhone:			
    						es = EntityState.Modified;
    					}
    					//
    					//	Update Player 
    					//
    					upd.nShirtSize			= cplayer.nShirtSize;
    					upd.PlayerRating		= cplayer.PlayerRating;
    					upd.Sanction			= cplayer.Sanction;
    					upd.SanctionExpires		= cplayer.SanctionExpires;
    					upd.SanctionFee			= cplayer.SanctionFee;
    					upd.SanctionLeagueID	= cplayer.SanctionLeagueID;
    					//
    					//	Update person
    					//
    					upd.Person.PrefixName	= cplayer.Person.PrefixName;
    					upd.Person.GivenName	= cplayer.Person.GivenName;
    					upd.Person.MiddleName	= cplayer.Person.MiddleName;
    					upd.Person.SurName		= cplayer.Person.SurName;
    					upd.Person.SuffixName	= cplayer.Person.SuffixName;
    					upd.Person.NickName		= cplayer.Person.NickName;
    					upd.Person.DOB			= cplayer.Person.DOB;
    					upd.Person.Sex			= cplayer.Person.Sex;
    
    					db2.SaveChanges();
    					return RedirectToAction("Index");
    				}
    

    Now the updates are done as I would expect. What I was hoping was that there was a standard routine (UpdateModel) that would take care of the routine moving of fields.

    I have tried alot of variations on UpdateModel (White/Black list) but they all cause my original problem to happen.

    Even though this method contains more code it does give exact handling of the Field from the form.

    Can UpdateModel be used to accomplish the same as above?

    • Edited by SNorberg Thursday, August 11, 2011 1:28 PM More information
    Thursday, August 11, 2011 11:35 AM

Answers

  • Hi Scott,

    I write a sample, it seems work on my computer, please feel free to have a test:

     

    public class CPlayer
     {
      public int Id { get; set; }
      public string Name { get; set; }
      public ICollection<Phone> Phones { get; set; }
     }
    
     public class Phone
     {
      public int Id { get; set; }
      public string Number { get; set; }
      public CPlayer Player { get; set; }
     }
    
     public class Context:DbContext
    
     {
      public DbSet<CPlayer> CPlayers { get; set; }
      public DbSet<Phone> Phones { get; set; }
     }
    
    //edit
     [HttpPost]
      public ActionResult Edit(int id, FormCollection collection)
      {
       try
       {
        // TODO: Add update logic here
        CPlayer play = context.CPlayers.Find(id);
        context.Entry(play).Collection(p => p.Phones).Load();
        var test = play.Phones;
        play.Phones.First().Number="99";
        UpdateModel<CPlayer>(play);
        context.SaveChanges();
        return RedirectToAction("Index");
       }
       catch
       {
        return View();
       }
      }
    
    
    I think this link is help.
    Have a nice day.

     


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    • Marked as answer by Alan_chen Tuesday, August 30, 2011 8:28 AM
    Wednesday, August 24, 2011 9:11 AM

All replies

  • Hi SNorberg,

    Welcome!

    To tell you truth, I'm not familiar with this controller. There are three ways to update records from EF.

    1. Select out the entity and change its properties then SaveChanges----your second way.

    2. Attach the entity to Context and set the entity's states to Modified----your first way.

    3. Run Command Text from Context.Database.SqlExcuteCommand();

    I test the code as follows in Console, it works.

     using (var context= new EFTestContext())
          {
            var detach = context.Employees.Include("Vacations").AsNoTracking().First();
            var vacations = detach.Vacations;
            foreach (var item in vacations)
            {
              item.Days = 100;
            }
    //Detach the entity from context and change it 
            context.Employees.Attach(detach);
            context.Entry(detach).State = System.Data.EntityState.Modified;
            foreach (var item in vacations)
            {
              context.Entry(item).State = System.Data.EntityState.Modified;
            }
            context.SaveChanges();
          }
    


    BTW, If your problem relates to the control, I think you should repost here: http://forums.asp.net/1146.aspx

    Have a nice day.


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, August 12, 2011 8:08 AM
  • Alan,

    The problem here has nothing to do with the controller, it is strickly EF / ADO, which I thought was this group? So reposting this back to the asp forum, which is where I started, is just a dodge.

    You didn't even attempt to answer the basic question or point me somewhere that has the information.

    The question was can I somehow use UpdateModel to keep from moving individual fields when updating an entity. When I tried to use UpdateModel I was getting the original problem of inserting a new record and nulling out the FK on the record I was trying to update.

    Please spend a couple minutes with this rather than trying to deflect it to a different forum. I am just getting started with EF, and I have NOT decided whether or not it is worth the hassle to use or not. I have been using ADO and TSQL for years but the EF wrapper adds alot of restrictions and I am trying to come up with correct coding practices for EF.

    Sorry for my tone, but I was looking forward to a response with some insight and that just didn't happen.


    Scott
    Friday, August 12, 2011 1:03 PM
  • Hi Scott,

    I write a sample, it seems work on my computer, please feel free to have a test:

     

    public class CPlayer
     {
      public int Id { get; set; }
      public string Name { get; set; }
      public ICollection<Phone> Phones { get; set; }
     }
    
     public class Phone
     {
      public int Id { get; set; }
      public string Number { get; set; }
      public CPlayer Player { get; set; }
     }
    
     public class Context:DbContext
    
     {
      public DbSet<CPlayer> CPlayers { get; set; }
      public DbSet<Phone> Phones { get; set; }
     }
    
    //edit
     [HttpPost]
      public ActionResult Edit(int id, FormCollection collection)
      {
       try
       {
        // TODO: Add update logic here
        CPlayer play = context.CPlayers.Find(id);
        context.Entry(play).Collection(p => p.Phones).Load();
        var test = play.Phones;
        play.Phones.First().Number="99";
        UpdateModel<CPlayer>(play);
        context.SaveChanges();
        return RedirectToAction("Index");
       }
       catch
       {
        return View();
       }
      }
    
    
    I think this link is help.
    Have a nice day.

     


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    • Marked as answer by Alan_chen Tuesday, August 30, 2011 8:28 AM
    Wednesday, August 24, 2011 9:11 AM
  • Hi,

    I am writing to check the status of the issue on your side.  Would you mind letting us know the result of the suggestions?

    If you need further assistance, please feel free to let me know.   I will be more than happy to be of assistance.

    Have a nice day.


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, August 26, 2011 3:08 AM