none
Saving a changed Entity also saves associated entities RRS feed

  • Question

  • Hello,

    I'm using EF 4.2 with DbContext and Poco Entities and I'm experiencing a strange behaviour when saving a changed entity. Assume i have a entity "Mitarbeiter" where i change a single property "deaktiviert" to true. When i call dbcontext.savechanges, EF not only saves the changed entity, but also the associated entities "Anrede", "Titel" and "Kunde". Is this a normal behaviour and why is it like it is? When ii query the Changetracker before saving the changes for all changed entrys (dbcontext.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged) it returns only the changed entity.

    Here is one of my POCO Entities:

    namespace SigiNX.Entities
    {
        using System;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.Runtime.Serialization;
        
        [DataContract]
        [Serializable]
        public partial class Mitarbeiter : INotifyPropertyChanged 
        {
        	public Mitarbeiter()
        	{
        		this.Leistungsschein = new HashSet<Leistungsschein>();
        		this.Veranstaltungsanmeldung = new HashSet<Veranstaltungsanmeldung>();
        		this.Zustaendigkeit = new HashSet<Zustaendigkeit>();
        	}
        
        	[DataMember]
        	private int _MitarbeiterId;
        
        	[DataMember]
        	public int MitarbeiterId 
        	{ 
        		get
        		{
        			return  _MitarbeiterId;
        		}
        	
        		set
        		{
        			if (_MitarbeiterId != value)
        			{
        				_MitarbeiterId = value;
        				OnPropertyChanged("MitarbeiterId");
        			}
        		}
        	}
        
        	[DataMember]
        	private int _KundeId;
        
        	[DataMember]
        	public int KundeId 
        	{ 
        		get
        		{
        			return  _KundeId;
        		}
        	
        		set
        		{
        			if (_KundeId != value)
        			{
        				_KundeId = value;
        				OnPropertyChanged("KundeId");
        			}
        		}
        	}
        
        	[DataMember]
        	private Nullable<int> _AnredeId;
        
        	[DataMember]
        	public Nullable<int> AnredeId 
        	{ 
        		get
        		{
        			return  _AnredeId;
        		}
        	
        		set
        		{
        			if (_AnredeId != value)
        			{
        				_AnredeId = value;
        				OnPropertyChanged("AnredeId");
        			}
        		}
        	}
        
        	[DataMember]
        	private Nullable<int> _TitelId;
        
        	[DataMember]
        	public Nullable<int> TitelId 
        	{ 
        		get
        		{
        			return  _TitelId;
        		}
        	
        		set
        		{
        			if (_TitelId != value)
        			{
        				_TitelId = value;
        				OnPropertyChanged("TitelId");
        			}
        		}
        	}
        
        	[DataMember]
        	private string _Vorname;
        
        	[DataMember]
        	public string Vorname 
        	{ 
        		get
        		{
        			return  _Vorname;
        		}
        	
        		set
        		{
        			if (_Vorname != value)
        			{
        				_Vorname = value;
        				OnPropertyChanged("Vorname");
        			}
        		}
        	}
        
        	[DataMember]
        	private string _Nachname;
        
        	[DataMember]
        	public string Nachname 
        	{ 
        		get
        		{
        			return  _Nachname;
        		}
        	
        		set
        		{
        			if (_Nachname != value)
        			{
        				_Nachname = value;
        				OnPropertyChanged("Nachname");
        			}
        		}
        	}
        
        	[DataMember]
        	private string _Durchwahl;
        
        	[DataMember]
        	public string Durchwahl 
        	{ 
        		get
        		{
        			return  _Durchwahl;
        		}
        	
        		set
        		{
        			if (_Durchwahl != value)
        			{
        				_Durchwahl = value;
        				OnPropertyChanged("Durchwahl");
        			}
        		}
        	}
        
        	[DataMember]
        	private string _Mobilnummer;
        
        	[DataMember]
        	public string Mobilnummer 
        	{ 
        		get
        		{
        			return  _Mobilnummer;
        		}
        	
        		set
        		{
        			if (_Mobilnummer != value)
        			{
        				_Mobilnummer = value;
        				OnPropertyChanged("Mobilnummer");
        			}
        		}
        	}
        
        	[DataMember]
        	private string _Email;
        
        	[DataMember]
        	public string Email 
        	{ 
        		get
        		{
        			return  _Email;
        		}
        	
        		set
        		{
        			if (_Email != value)
        			{
        				_Email = value;
        				OnPropertyChanged("Email");
        			}
        		}
        	}
        
        	[DataMember]
        	private bool _Deaktiviert;
        
        	[DataMember]
        	public bool Deaktiviert 
        	{ 
        		get
        		{
        			return  _Deaktiviert;
        		}
        	
        		set
        		{
        			if (_Deaktiviert != value)
        			{
        				_Deaktiviert = value;
        				OnPropertyChanged("Deaktiviert");
        			}
        		}
        	}
        
        	[DataMember]
        	private byte[] _Bild;
        
        	[DataMember]
        	public byte[] Bild 
        	{ 
        		get
        		{
        			return  _Bild;
        		}
        	
        		set
        		{
        			if (_Bild != value)
        			{
        				_Bild = value;
        				OnPropertyChanged("Bild");
        			}
        		}
        	}
        
        	[DataMember]
        	private System.DateTime _UpdateDatum;
        
        	[DataMember]
        	public System.DateTime UpdateDatum 
        	{ 
        		get
        		{
        			return  _UpdateDatum;
        		}
        	
        		set
        		{
        			if (_UpdateDatum != value)
        			{
        				_UpdateDatum = value;
        				OnPropertyChanged("UpdateDatum");
        			}
        		}
        	}
        
        	[DataMember]
        	private string _UpdateBenutzer;
        
        	[DataMember]
        	public string UpdateBenutzer 
        	{ 
        		get
        		{
        			return  _UpdateBenutzer;
        		}
        	
        		set
        		{
        			if (_UpdateBenutzer != value)
        			{
        				_UpdateBenutzer = value;
        				OnPropertyChanged("UpdateBenutzer");
        			}
        		}
        	}
        
        	[DataMember]
        	private byte[] _TimeStamp;
        
        	[DataMember]
        	public byte[] TimeStamp 
        	{ 
        		get
        		{
        			return  _TimeStamp;
        		}
        	
        		set
        		{
        			if (_TimeStamp != value)
        			{
        				_TimeStamp = value;
        				OnPropertyChanged("TimeStamp");
        			}
        		}
        	}
        
        
        	[DataMember]
        	private Anrede _Anrede;
        
        	[DataMember]
        	public virtual Anrede Anrede 
        	{ 
        		get
        		{
        			return  _Anrede;
        		}
        	
        		set
        		{
        			if (_Anrede != value)
        			{
        				_Anrede = value;
        				OnPropertyChanged("Anrede");
        			}
        		}
        	}
        
        	[DataMember]
        	private Kunde _Kunde;
        
        	[DataMember]
        	public virtual Kunde Kunde 
        	{ 
        		get
        		{
        			return  _Kunde;
        		}
        	
        		set
        		{
        			if (_Kunde != value)
        			{
        				_Kunde = value;
        				OnPropertyChanged("Kunde");
        			}
        		}
        	}
        
        	[DataMember]
        	private ICollection<Leistungsschein> _Leistungsschein;
        
        	[DataMember]
        	public virtual ICollection<Leistungsschein> Leistungsschein 
        	{ 
        		get
        		{
        			return  _Leistungsschein;
        		}
        	
        		set
        		{
        			if (_Leistungsschein != value)
        			{
        				_Leistungsschein = value;
        				OnPropertyChanged("Leistungsschein");
        			}
        		}
        	}
        
        	[DataMember]
        	private Titel _Titel;
        
        	[DataMember]
        	public virtual Titel Titel 
        	{ 
        		get
        		{
        			return  _Titel;
        		}
        	
        		set
        		{
        			if (_Titel != value)
        			{
        				_Titel = value;
        				OnPropertyChanged("Titel");
        			}
        		}
        	}
        
        	[DataMember]
        	private ICollection<Veranstaltungsanmeldung> _Veranstaltungsanmeldung;
        
        	[DataMember]
        	public virtual ICollection<Veranstaltungsanmeldung> Veranstaltungsanmeldung 
        	{ 
        		get
        		{
        			return  _Veranstaltungsanmeldung;
        		}
        	
        		set
        		{
        			if (_Veranstaltungsanmeldung != value)
        			{
        				_Veranstaltungsanmeldung = value;
        				OnPropertyChanged("Veranstaltungsanmeldung");
        			}
        		}
        	}
        
        	[DataMember]
        	private ICollection<Zustaendigkeit> _Zustaendigkeit;
        
        	[DataMember]
        	public virtual ICollection<Zustaendigkeit> Zustaendigkeit 
        	{ 
        		get
        		{
        			return  _Zustaendigkeit;
        		}
        	
        		set
        		{
        			if (_Zustaendigkeit != value)
        			{
        				_Zustaendigkeit = value;
        				OnPropertyChanged("Zustaendigkeit");
        			}
        		}
        	}
        
        
        	#region INotifyPropertyChanged Members
        
        	/// <summary>
        	/// Raised when a property on this object has a new value.
        	/// </summary>
        	public event PropertyChangedEventHandler PropertyChanged;
        
        	/// <summary>
        	/// Raises this object's PropertyChanged event.
        	/// </summary>
        	/// <param name="propertyName">The property that has a new value.</param>
        	protected virtual void OnPropertyChanged(string propertyName)
        	{
        		PropertyChangedEventHandler handler = this.PropertyChanged;
        		if (handler != null)
        		{
        			var e = new PropertyChangedEventArgs(propertyName);
        			handler(this, e);
        		}
        	}
        
        	#endregion
        
        }
    }
    
    Saturday, January 14, 2012 11:22 AM

Answers

  • Hi mcgu,

    Sorry for late reply, I have a rest at weekends.

    I test again, and I can repro the issue. If we load the related entities to client, even only modified one entity, the related entities are still update. I'm not sure what the root cause is. After searching, I find a similar case, please refer to it, I hope it could help you.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us
    • Marked as answer by mcgu Monday, January 23, 2012 10:45 AM
    Monday, January 23, 2012 2:51 AM
    Moderator

All replies

  • Hi mcgu,

    Welcome to MSDN Forum.

    Could you please tell me how did you determine the associated entities are updated too? I try to create two Entities, 'Player' and 'Team', the relationship between them is many to many. In the Team POCO class, I add a property byte[] rowVersion and designate it as timeStamp. When I update the player entity, I found the rowVersion field is not modified, if the team entity is updated too, this field should be modified.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us
    Monday, January 16, 2012 7:03 AM
    Moderator
  • Hello,

    i don't have any problems with the many to many relationships, but with the one/zero to many. The problem occured, when i implemented the optimistic concurrency. I'm using the column Timestamp for concurrency checking in EF. I first realized the problem while testing the concurrency handling.

    I opened an entry in my application, while this entry was open i changed a single value of the same entry using a second window with it's own dbcontext and saved the changes to the db. Then i changed a value in the open entry in window 1 an saved it. As expected, the save method threw an concurrency exception.

    So now my default behaviour (simplified) was to reload this entry and then save it. But then i realized, that not only the changed entry threw an concurrency exception, but all related entries as well because they have been written to the database during the changes i made in the second window.

    I have some entities with a lot of relations and with the current problem that means that i have to do a lot of reloading in the case of an concurrency exception. And the fact, that the concurrency exception doesn't return all confilcted entries at once makes life even harder because i have to call savechanges very often to go trough all the conflicted entries.

    Is there somithing wrong with my code or is this the default behaviour of EF.

     

    greetings

    Michael

    Wednesday, January 18, 2012 5:37 PM
  • Hi mcgu,

    My mistake in the above post, the relationship between Team and Player is one to many. Even I modified the relationship to 0..1 to many, after modifying the record of Team table and submit to database, the timestamp of the Player is still the same. I have another method you can have a try. Before update the object, you can detach the related objects, then the related objects will not update.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us
    Thursday, January 19, 2012 7:38 AM
    Moderator
  • Hello Allen,

    i tried it once more. When i change a property of the enry player, and save the changes the related team get's updated as well, but only if the team related is loaded before saving the changes (for example via WriteLine(player.team.name)). I created a small sample project that shows the problem, is there a way to upload it here? The Project also uses my modified T4 Template for Entity generation.

    Your solution with detaching the related entries sounds like a lot of individual work. Right now a i have a baseclass that handles all the saving, when i need to detach all related entries before saving, i can't use my baseclass because i need to individual define all entries that need to be detached individually for every entity. Or is there a generic approach for getting all navigation properties?

    best regards!

    Friday, January 20, 2012 7:25 AM
  • Hi mcgu,

    Please send the demo to v-alll@microsoft.com , I will test the code and response here as soon as possible.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us
    Friday, January 20, 2012 7:45 AM
    Moderator
  • Hi mcgu,

    I have reproduced the issue. I think the root cause is the T4 templete, with the same models, I create a new project with the same code in your main method, no matter whether call Console.WriteLine() method to print the Team's name or not, the team will not update. Certainly, without the T4 template, I can't overwrite the SaveChanges method, so I didn't print out the count of changed entites, I only trace the timestamp of the team table. I'm afraid I'm not familiar with T4 template, so I couldn't find where is wrong of it. Sorry for little help.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us
    Friday, January 20, 2012 9:49 AM
    Moderator
  • Hi!

    First of all, thanks for your effort!

    Have you set the concurrency mode of TimeStamp to fixed?

    I also tried to recreate the project - created the model from the db, and left everything at default values (this time using objectcontext). Everthing works fine without the concurrencymode, but when you set the concurrency mode to fixed, the related entries are updated in the database.

    greetings
    Michael

    Friday, January 20, 2012 10:19 AM
  • Hi mcgu,

    Sorry for late reply, I have a rest at weekends.

    I test again, and I can repro the issue. If we load the related entities to client, even only modified one entity, the related entities are still update. I'm not sure what the root cause is. After searching, I find a similar case, please refer to it, I hope it could help you.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us
    • Marked as answer by mcgu Monday, January 23, 2012 10:45 AM
    Monday, January 23, 2012 2:51 AM
    Moderator
  • Hi,

     

    thanks again. It seems like the bug has been fixed in EF CTP5. I'm going for that.

     

    best regards!

    Michael

    Monday, January 23, 2012 10:45 AM