none
Insert new child on insert of the parent over a 1-N association RRS feed

  • Question

  • Hi all,

    I have a strange behaviour of the automatic insert of child entities on the insert of the parent entity.

    Here is a description of my entities :
    Entity A = IdA (primary key), Bs (1-N association to child entities B), other properties ...
    Entity B = IdB (primary key), ParentA (1-1 association to parent entity A), other properties ...

    Test case 1 : successful

    1. I create a new entity A,
    2. I add a new child B,
    3. I insert A.

    I have the new A created and the new B created in the database.

    // ***** prepare entities *****
    a = new A { IdA = "3.7", Name = "Numero37" };
    a.Bs.Add(new B { IdB = "10.7" } );
    
    // ***** query database *****
    DataContext dctx = ...;
    dctx.GetTable<A>().InsertOnSubmit(a);
    dctx.SubmitChanges();
    
    

    Test case 2 : failure

    1. I create a new entity A,
    2. I add a new child B,
    3. I add an already existing child B,
    4. I insert A.

    I have the new A created and the already existing B updated to be linked to the new A
    BUT the new B was not created in the database.

    // ***** prepare entities *****
    a = new A { IdA = "3.7", Name = "Numero37" };
    a.Bs.Add(new B { IdB = "10.7" } );
    a.Bs.Add(BHelper.GetBById("10.8")); // get a reference to the existing B entity
    
    // ***** query database *****
    DataContext dctx = ...;
    dctx.GetTable<A>().InsertOnSubmit(a);
    dctx.SubmitChanges();
    
    

    Is this a normal behaviour ?
    Is there something to do to have this issue fixed ?

    Thanks for your help,
    Christophe

    Wednesday, May 13, 2009 11:31 AM

Answers

  • I founded where the error came from = the override of my "Equals" and "GetHashCode" methods where incorrect.
    To simplify, all my B entities where considered as Equals = when I added an existing B and a new B as children of the new A he would consider having only 1 B child because the "Equals" method would say so !!!

    After correction of the "GetHashCode" method, the behaviour is normal = my new B is created and the existing B is liked correctly.

    Thank you all for your help.

    By the way, is there a strong manner of writing the "Equals" and "GetHashCode" methods ?
    Here is the code I use today :

    public override bool Equals( object obj )
    {
    	if( this == obj ) return true; // check if same reference
    	if( ( obj == null ) || !( obj is B ) ) return false; // check if right instance
    
    	B castObj = (B)obj;
    	return castObj.GetHashCode() == this.GetHashCode();
    }
    
    public override int GetHashCode()
    {
    	int hash = 57;
    // modify the hash code on each property participating in the primary key hash = 27 * hash * (this._id_b != null ? this._id_b.GetHashCode() : 1); return hash; }
    Monday, May 18, 2009 9:30 AM
  • Your are right about the GetHashCode that does not ensure equality between 2 entities.
    For this I changed my Equal method yesterday night to this :

    public override bool Equals( object obj )
    {
    	if( this == obj ) return true;
    	if( ( obj == null ) || !( obj is A ) ) return false;
    
    	A castObj = (A)obj;
    
    	bool result = true;
    
    	result = (result && (
    (this._id_a == null && castObj._id_a == null) || (this._id_a != null && this._id_a.Equals(castObj._id_a))
    )); result = (result && (
    ((this._name == null && castObj._name == null) || (this._name != null && this._name.Equals(castObj._name))
    )); return result; }

    Thanks a lot for your help

    Christophe

    Tuesday, May 19, 2009 9:02 AM

All replies

  • How did the 'existing B' entity come about? Did you already get it from an existing database B? Or is a new object? If it is not new...who is its existing Parent?


    William Wegerson (www.OmegaCoder.Com)
    Thursday, May 14, 2009 3:05 AM
    Moderator
  • the 'existing B' arrives from the database throw the same DataContext.
    Here is the associated piece of code :

    public static B GetBById(String id_b)
    {
    	try
    	{
    		DataContext dctx = ...;
    
    		// ***** query database *****
    		var results =
    			from	b in dctx.GetTable<B>()
    			where	(
    						b.IdB == id_b
    			)
    			select b;
    
    		// ***** check results *****
    		if (results.Count() == 0)
    			throw new UnknownBException("unknown B with specified ID");
    
    		if (results.Count() > 1)
    			throw new MultipleBException("multiple B with specified ID");
    
    		return (B)results.Single();
    	}
    	catch (Exception e)
    	{
    		...
    
    		throw;
    	}
    }
    

     

    If you need the hole code bundle, I can provide it for you

    Thanks,

    Christophe

    Thursday, May 14, 2009 7:46 AM
  • Ok, so you are trying to change B's association to its target A? Don't you need to change the foreign key id which specifies A on the B and not have to add it to A?

    William Wegerson (www.OmegaCoder.Com)
    Thursday, May 14, 2009 3:03 PM
    Moderator
  • I have no problem with the existing B = the link with the new A parent is done correctely.

    I have a problem with the new B child :
    - that is created in the first test case when it is alone in the child collection Bs,
    - but that is not created at all in the second test case when another exiting B child is with it in the child collection.

    Do you have any explanation about this behaviour ?

    Thank you for you answears.
    Christophe
    Friday, May 15, 2009 8:50 AM
  • I can't reproduce what you're seeing in .NET 3.5 SP1. I know that if I use the same DataContext to retrieve the existing B AND to insert the new A and the new B, the new B is added to the database and the existing B has its relationship updated to the new A.

    If I use two distinct DataContext instances--one to retrieve the existing B and another to insert the new A and call SubmitChanges on--then two new Bs are inserted in the database.

    Neither of those matches what you're saying though. However, have you tried using the same DataContext instance for both retrieving the old B and adding the new A?
    Sunday, May 17, 2009 4:52 PM
    Answerer
  • I founded where the error came from = the override of my "Equals" and "GetHashCode" methods where incorrect.
    To simplify, all my B entities where considered as Equals = when I added an existing B and a new B as children of the new A he would consider having only 1 B child because the "Equals" method would say so !!!

    After correction of the "GetHashCode" method, the behaviour is normal = my new B is created and the existing B is liked correctly.

    Thank you all for your help.

    By the way, is there a strong manner of writing the "Equals" and "GetHashCode" methods ?
    Here is the code I use today :

    public override bool Equals( object obj )
    {
    	if( this == obj ) return true; // check if same reference
    	if( ( obj == null ) || !( obj is B ) ) return false; // check if right instance
    
    	B castObj = (B)obj;
    	return castObj.GetHashCode() == this.GetHashCode();
    }
    
    public override int GetHashCode()
    {
    	int hash = 57;
    // modify the hash code on each property participating in the primary key hash = 27 * hash * (this._id_b != null ? this._id_b.GetHashCode() : 1); return hash; }
    Monday, May 18, 2009 9:30 AM
  • Glad you figured out the problem. I'm not sure what you mean by a strong manner of writing Equals and GetHashCode. If you mean strongly typed manner you can implement the IEquatable<T> interface to facilitate equality checking. Another observation is that you shouldn't treat the results of GetHashCode as equivalent. Two objects of the same Type that are equal must have the same hash code, but two objects of the same Type that return the same hash code may not be equal. You can read more at the Object.GetHashCode method documentation.

    Here's an example of using IEquatable<T>.

    public class Foo : IEquatable<Foo>
    {
        private int _id;
    
        public bool Equals(Foo other)
        {
            if (other == null)
            {
                return false;
            }
    
            return other._id == this._id;
        }
    
        public override bool Equals(object obj)
        {
            return this.Equals(obj as Foo);
        }
    
        public override int GetHashCode()
        {
            return _id.GetHashCode();
        }
    }
    Tuesday, May 19, 2009 1:03 AM
    Answerer
  • Your are right about the GetHashCode that does not ensure equality between 2 entities.
    For this I changed my Equal method yesterday night to this :

    public override bool Equals( object obj )
    {
    	if( this == obj ) return true;
    	if( ( obj == null ) || !( obj is A ) ) return false;
    
    	A castObj = (A)obj;
    
    	bool result = true;
    
    	result = (result && (
    (this._id_a == null && castObj._id_a == null) || (this._id_a != null && this._id_a.Equals(castObj._id_a))
    )); result = (result && (
    ((this._name == null && castObj._name == null) || (this._name != null && this._name.Equals(castObj._name))
    )); return result; }

    Thanks a lot for your help

    Christophe

    Tuesday, May 19, 2009 9:02 AM