1:1 related entity not getting its ID set on save

Posts   
 
    
danh
User
Posts: 70
Joined: 16-Jul-2007
# Posted on: 03-Jun-2008 17:48:39   

Hi,

I have found what seems to be a bug in 2.6 beta - or at least behaviour that is different from 2.5. I have a newly-created Person entity, which has a ReferringAgent entity linked by their ID fields in a 1:1 relation. I save the Person entity non-recursively, and not refetching from the database. Its Id field is updated to the new identity value, correctly. I then save the related ReferringAgent entity, but this fails with the following exception:


An exception was caught during the execution of an action query: Duplicate entry '0' for key 1. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception.
   at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute() in F:\Source code\LLBLGen Pro\2.6\Sourcecode\RuntimeLibraries\Net2.x\ORMSupportClasses\Query\ActionQuery.cs:line 197

Inner exception:


"Duplicate entry '0' for key 1"
   at CoreLab.MySql.bc.n()
   at CoreLab.MySql.bc.c()
   at CoreLab.MySql.c.a(f[]& A_0, Int32& A_1)
   at CoreLab.MySql.c.a(Byte[] A_0, Int32 A_1, Boolean A_2)
   at CoreLab.MySql.ab.e()
   at CoreLab.MySql.ab.o()
   at CoreLab.MySql.MySqlCommand.a(CommandBehavior A_0, IDisposable A_1, Int32 A_2, Int32 A_3)
   at CoreLab.Common.DbCommandBase.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.ExecuteReader()
   at CoreLab.Common.DbCommandBase.ExecuteNonQuery()
   at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute() in F:\Source code\LLBLGen Pro\2.6\Sourcecode\RuntimeLibraries\Net2.x\ORMSupportClasses\Query\ActionQuery.cs:line 168

I can see the cause: the ReferringAgent entity doesn't have its ID set when the Person entity is saved, thus it tries to save a zero in the database, which fails due to FK constraints.

This worked correctly in 2.5. The following code snippet shows a reproduction of my problem:


            var person = new PersonEntity();
            person.FirstName = "someone";

            person.ReferringAgent = new ReferringAgentEntity();
            person.ReferringAgent.AffiliateWebsiteFolder = "something";

            using (DataAccessAdapter adapter = new DataAccessAdapter())
            {
                // V2.5: person.Id == 0, person.ReferringAgent.Id == 0
                // V2.6: person.Id == 0, person.ReferringAgent.Id == 0

                adapter.SaveEntity(person, true, false);

                // V2.5: person.Id == 12345, person.ReferringAgent.Id == 12345
                // V2.6: person.Id == 12345, person.ReferringAgent.Id == 0

                adapter.SaveEntity(person.ReferringAgent, true, false);
                // ^ V2.6: Throws exception because we try to save person.ReferringAgent with ID == 0
            }

I am using 2.6 beta (May 15th 2008 ), adapter, .NET 3.5

Thanks, Dan

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39872
Joined: 17-Aug-2003
# Posted on: 03-Jun-2008 18:20:18   

Will look into it. It doesn't look like behavior which was changed, the related entity simply should get the FK synced. It could be a bug indeed, as the 1:1 sync stuff was updated with code differently from the v2.5 one which had a template based piece of code for this.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39872
Joined: 17-Aug-2003
# Posted on: 04-Jun-2008 11:41:07   

I can't reproduce it:


[Test]
public void OneToOnePkPkSyncTest()
{
    EmployeeEntity e = (EmployeeEntity)new EmployeeEntityFactory().Create();
    e.Name = "John Doe";
    e.StartDate = DateTime.Now;
    e.ClerkCore = new ClerkCoreEntity();
    e.ClerkCore.JobDescription = "Pushing paper";

    using(DataAccessAdapter adapter = new DataAccessAdapter())
    {
        Assert.IsTrue(adapter.SaveEntity(e, true, false));
        Assert.IsFalse(e.IsNew);
        Assert.IsTrue(e.Id>0);
        Assert.IsTrue(adapter.SaveEntity(e.ClerkCore, true, false));
        Assert.IsFalse(e.ClerkCore.IsNew);
        Assert.IsTrue(e.ClerkCore.ClerkId>0);

        adapter.DeleteEntity(e.ClerkCore);
        adapter.DeleteEntity(e);
    }
}

Employee.Id (pk) 1:1 ClerkCore.ClerkId (pk) Employee.Id is identity field, ClerkCore.ClerkId isn't (of course).

Frans Bouma | Lead developer LLBLGen Pro
danh
User
Posts: 70
Joined: 16-Jul-2007
# Posted on: 05-Jun-2008 12:20:53   

Hmm... I have just installed the RC and re-tested, results are the same.

In my database, however, PersonEntity is a subtype of BaseEntity (sorry, I should have mentioned this in my original post.. flushed ) - if this makes a difference?

Base.Id (pk, identity) 1 : 1 Person.Id (pk) Person.Id (pk) 1 : 1 ReferringAgent.Id (pk)

Is there any other information I can give you to help you reproduce this?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39872
Joined: 17-Aug-2003
# Posted on: 05-Jun-2008 13:11:30   

danh wrote:

Hmm... I have just installed the RC and re-tested, results are the same.

In my database, however, PersonEntity is a subtype of BaseEntity (sorry, I should have mentioned this in my original post.. flushed ) - if this makes a difference?

Yes. Person's PK gets synced by the fact that it's PK is linked with the pk of base entity. So it might be that there's no event raised properly. I'll do some further checking. I have such a hierarchy in my inheritance db (employee - manager - boardmember) so if I make manager a subtype of employee and boardmember not a subtype of manager, I have the same hierarchy.

Base.Id (pk, identity) 1 : 1 Person.Id (pk) Person.Id (pk) 1 : 1 ReferringAgent.Id (pk) Is there any other information I can give you to help you reproduce this?

I think I can build the same hierarchy and see if that makes a difference.

Btw, why isn't referringAgent a subtype of person?

Frans Bouma | Lead developer LLBLGen Pro
danh
User
Posts: 70
Joined: 16-Jul-2007
# Posted on: 05-Jun-2008 13:19:33   

Otis wrote:

Btw, why isn't referringAgent a subtype of person?

Because I have several other tables (SellingAgent, User, Client etc.) and each person can be 1 or several of these varieties (eg. a selling agent AND a client).

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39872
Joined: 17-Aug-2003
# Posted on: 05-Jun-2008 13:26:50   

danh wrote:

Otis wrote:

Btw, why isn't referringAgent a subtype of person?

Because I have several other tables (SellingAgent, User, Client etc.) and each person can be 1 or several of these varieties (eg. a selling agent AND a client).

Ok simple_smile

btw, I still can't reproduce it:


[Test]
public void OneToOnePkPkSyncTest()
{
    ManagerEntity m = new ManagerEntity();
    m.Name = "John Doe";
    m.StartDate = DateTime.Now;
    m.BoardMemberCore = new BoardMemberCoreEntity();

    using(DataAccessAdapter adapter = new DataAccessAdapter())
    {
        Assert.IsTrue(adapter.SaveEntity(m, true, false));
        Assert.IsFalse(m.IsNew);
        Assert.IsTrue(m.Id>0);
        Assert.IsTrue(adapter.SaveEntity(m.BoardMemberCore, true, false));
        Assert.IsFalse(m.BoardMemberCore.IsNew);
        Assert.IsTrue(m.BoardMemberCore.BoardMemberId>0);

        adapter.DeleteEntity(m.BoardMemberCore);
        adapter.DeleteEntity(m);
    }
}

Works OK. manager is subtype of employee, which has an identity pk. Boardmembercore has a 1:1 pk-pk relation with manager. Boardmember and manager are saved correctly:


    Query: INSERT INTO [InheritanceOne].[dbo].[Employee] ([Name], [StartDate])  VALUES (@Name, @StartDate);SELECT @Id_EmployeeEntity=SCOPE_IDENTITY()
    Parameter: @Id_EmployeeEntity : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Output. Value: <undefined value>.
    Parameter: @Name : AnsiString. Length: 50. Precision: 0. Scale: 0. Direction: Input. Value: "John Doe".
    Parameter: @StartDate : DateTime. Length: 0. Precision: 23. Scale: 3. Direction: Input. Value: 5-6-2008 13:16:27.


    Query: INSERT INTO [InheritanceOne].[dbo].[Manager] ([ManagerID])  VALUES (@Id)
    Parameter: @Id : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: <undefined value>.

    Query: INSERT INTO [InheritanceOne].[dbo].[BoardMember] ([BoardMemberID])  VALUES (@BoardMemberId)
    Parameter: @BoardMemberId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 48350.

(the reason the query to insert manager has <undefined value> for the id is because the query is generated (and traced) before the values are synced.)

So ... I'm a bit puzzled what could be the reason it doesn't work for you... You use MySql, but nothing really is mysql specific in this case, as the id value is read back properly after the first insert, as you say.

Please make sure all code is re-generated as well, and all files are overwritten.

Frans Bouma | Lead developer LLBLGen Pro
danh
User
Posts: 70
Joined: 16-Jul-2007
# Posted on: 05-Jun-2008 14:37:51   

I've made sure everything is regenerated as you asked, re-tested, and still the same error. The generated files are being overwritten correctly.

In order to test, I have also tried deleting all the generated code and folders, and starting a new project in the designer. I generated DAL code with only the 3 involved entities (Base, Person and ReferringAgent), and tested with the sample code I showed you - still the same.

Would a trace be helpful?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39872
Joined: 17-Aug-2003
# Posted on: 05-Jun-2008 14:59:47   

danh wrote:

I've made sure everything is regenerated as you asked, re-tested, and still the same error. The generated files are being overwritten correctly.

In order to test, I have also tried deleting all the generated code and folders, and starting a new project in the designer. I generated DAL code with only the 3 involved entities (Base, Person and ReferringAgent), and tested with the sample code I showed you - still the same.

Would a trace be helpful?

If you could email us the testproject with the 3 entities, the DDL SQL for mysql so I can recreate the DB and if possible the .lgp file for the 3 entities I can try to reproduce it here and see what's different from my repro case.

Please email to support AT llblgen DOT com.

Frans Bouma | Lead developer LLBLGen Pro
danh
User
Posts: 70
Joined: 16-Jul-2007
# Posted on: 05-Jun-2008 16:50:49   

I've found the problem - on all my person variety entities (ReferringAgent, Client, User etc.) one side of the 1:1 relation with Person was hidden, so the PersonEntity.AfterSave event wasn't being subscribed to by any of them - hence, no propagation of the ID. Although I don't recall hiding these in the designer, I must have done so around the time I switched to 2.6 beta, as everything was working ok a week ago.

Although I have fixed the cause of the problem, I wonder if this is the correct/expected behaviour? It seems a little strange to me.. perhaps there should be a warning in the documentation (if there isn't already) that hiding one side of a 1:1 relation breaks the link between them in this way? Please correct me if I'm wrong! wink

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39872
Joined: 17-Aug-2003
# Posted on: 05-Jun-2008 17:08:16   

In old versions (before v2) we had the rule that the PK side decided to which entities it synced, so if you hid the relation PK -> FK, it didn't sync even though FK -> PK was visible.

In v2 and up, we changed this as it wasn't really logical: the FK decides to what to sync, as it's an observer to the PK side.

So if you hide the PK -> FK relation, no problem, but if you hide the FK -> PK relation, for the FK there's no PK side (as the relation is hidden!) so it won't sync.

I do think it's in the manual somewhere, but I've to look it up.

Frans Bouma | Lead developer LLBLGen Pro
danh
User
Posts: 70
Joined: 16-Jul-2007
# Posted on: 05-Jun-2008 17:10:38   

Yes, I suppose that makes sense. simple_smile Thanks for your help.