none
Unable to determine the principal end of 'foreign key name' relationship. Multiple added entities may have the same primary key. RRS feed

  • Question

  • Problem I'm having is in the subject line.  I have looked at the following:

    http://social.msdn.microsoft.com/Forums/en/adonetefx/thread/8eb12b1a-9296-491b-afd5-e96cf5b17124

    http://social.msdn.microsoft.com/Forums/en/adonetefx/thread/cfe58f73-64f2-4628-8e66-24b2f455729b

    http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataservices/thread/189f6e96-3d95-4125-986d-6d36c1b9bdd9

    http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/566dce25-1359-440f-8eb3-bab1a7b8cd4c

    http://social.msdn.microsoft.com/Forums/en/wcf/thread/7dc7bc1a-efd3-4300-97ab-85c126ab3ec1

    ...but I still am not seeing a solution.  Actually the best advice I figured would work comes from these two:

    http://stackoverflow.com/questions/8530581/multiple-added-entities-may-have-the-same-primary-key-in-entity-famework

    http://e10.in/blog/ef4-multiple-added-entities-may-have-the-same-primary-key

    ...but even following that advice still doesn't solve the problem.

    Here is the table design:

    Here's a look at the relevant EF model:

    Entity Framework Model

    I transform each of these to a Data Transfer Object, as I don't like sending EF objects over the wire.  The client is a Silverlight app, and I'm using RIA services.

    Here's how I fetch - I just project into the Dto.Object types and these are what is sent over the wire :

    public IEnumerable<Dto.DealerServiceItem> GetDealerServiceItems(int dealerId)
            {
                return
                    _ctx.DealerServiceItems.Where(si => si.DealerId == dealerId)
                        .Select(si => new Dto.DealerServiceItem
                            {
                                DealerServiceItemId = si.Id,
                                Name = si.Name,
                                DealerId = si.DealerId,
                                DefaultPrice = si.DefaultPrice,
                                DealerMaintenanceItems = si.DealerMaintenanceItems.Select(mi => new Dto.DealerMaintenanceItem
                                    {
                                        DealerServiceItemId = mi.DealerServiceItemId,
                                        DealerMaintenanceItemId = mi.Id,
                                        ApplyToAll = mi.ApplyToAll,
                                        BeginMileage = mi.BeginMileage,
                                        EndMileage = mi.EndMileage,
                                        ModelEngineId = mi.ModelEngineId,
                                        NegTolerance = mi.NegTolerance,
                                        PosTolerance = mi.PosTolerance,
                                        RepeatInterval = mi.RepeatInterval
                                    })
                            }).ToList();
            }

    Now, on the client, I new up a bunch and try to send it back and persist it.  If I new up a single DealerServiceItem with multiple DealerMaintenanceItem objects, and persist -- no problem at all...it works

    But if I new up more than one DealerServiceItem object, each with their own collection of DealerMaintenanceItems and try to persist, I get the "same primary key" error.

    I tried creating my own negative id values on the client-side to avoid this.  This is what the code in the links above recommended as a workaround.

    private static int _seq1;
    private static int _seq2;
    
    public void AddOne() {
       // other properties elided for brevity
       var item = new DealerServiceItem
                    {
                        DealerServiceItemId = _seq1--
                    };
       var maintenanceItem = new DealerMaintenanceItem
                        {
                            DealerServiceItemId = item.DealerServiceItemId,
                            DealerMaintenanceItemId = _seq2--
                        };
       item.DealerMaintenanceItems.Add(maintenanceItem);
       _serviceItems.Add(item);
    }
    
    

    If I call AddOne() more than once in the client, and then try to persist, on the server, here's what I receive (I've highlighted the client set ids in color) -- if you work it out, you'll see that there is no ambiguity.  There's no conflict with any rows already existing in the database either, and each of the PK ID properties on each object is set to be StoreGeneratedPattern = Identity

    INSERT DealerServiceItem => DealerServiceItemId:0, DealerId:1, Name:cvbcvbcv, DefaultPrice:0

    INSERT DealerMaintenanceItem => DealerMaintenanceItemId:0, DealerServiceItemId:0, ApplyToAll:True, RepeatInterval:5000, BeginMileage:5000, EndMileage:150000, PosTolerance:0, NegTolerance:0, ModelEngineId:0 

    INSERT DealerServiceItem => DealerServiceItemId:-1, DealerId:1, Name:vbvbvb, DefaultPrice:0

    INSERT DealerMaintenanceItem => DealerMaintenanceItemId:-1, DealerServiceItemId:-1, ApplyToAll:True, RepeatInterval:5000, BeginMileage:5000, EndMileage:150000, PosTolerance:0, NegTolerance:0, ModelEngineId:0 

    A first chance exception of type 'System.Data.UpdateException' occurred in System.Data.Entity.dll
    2012-05-24 17:00:52,521 [7] ERROR System.Data.UpdateException: Unable to determine the principal end of the 'RecommendedMaintenanceModel.FK_DealerMaintenanceItem_DealerServiceItem' relationship. Multiple added entities may have the same primary key.

    Multiple added entities DO NOT have the same primary key.  Why do I get this error?  It is almost as if it is suggesting that it *may* fail so it just chooses to throw an exception <chuckle> regardless of whether it really would fail or not.

    On the server, here are my two Insert methods.  These are called in order of presentation by WCF RIA services framework, and at the end, the PersistChanges() is called which tries to commit it as a transaction.  The ChangeSet.Associate is supposed to associate the client objects with the stored version after a commit happens.  The commit never happens, b/c it just throws the exception above.

    public void InsertDealerServiceItem(Dto.DealerServiceItem item, ChangeSet changeset)
            {
                // new up a new entity
                var entity = new DealerServiceItem
                    {
                        DealerId = item.DealerId,
                        Name = item.Name,
                        DefaultPrice = item.DefaultPrice
                    };
    
                // add it to the context
                _ctx.DealerServiceItems.AddObject(entity);
    
                // associate the auto-generated id with the client object
                changeset.Associate(item, entity, (i, e) =>
                    {
                        i.DealerServiceItemId = e.Id;
                    });
            }


    public void InsertDealerMaintenanceItem(Dto.DealerMaintenanceItem item, ChangeSet changeset)
            {
                // create a new dealer maintenance item entity object
                var entity = new DealerMaintenanceItem
                    {
                        DealerServiceItemId = item.DealerServiceItemId,
                        ApplyToAll = item.ApplyToAll,
                        RepeatInterval = item.RepeatInterval,
                        BeginMileage = item.BeginMileage,
                        EndMileage = item.EndMileage,
                        PosTolerance = item.PosTolerance,
                        NegTolerance = item.NegTolerance,
                        ModelEngineId = item.ModelEngineId
                    };
    
                // add it to the context
                _ctx.DealerMaintenanceItems.AddObject(entity);
                
                // associate the auto-generated ids with the client objects
                changeset.Associate(item, entity, (mi, mie) =>
                    {
                        mi.DealerMaintenanceItemId = mie.Id;
                        mi.DealerServiceItemId = mie.DealerServiceItemId;
                    });
            }

    Please someone tell me how to work around this?  I know I can fix it if I commit them as separate transactions but that is inefficient and undesirable for other reasons.  There must be a way to resolve this?

    It puzzles me b/c when I explicitly set the ids on the objects before they are passed to the server to be committed...it is clear that there is no ambiguity, but the exception still happens.


    Thursday, May 24, 2012 9:37 PM

Answers

  • Solved.  In my Insert methods, I had forgotten to copy the client-generated sequence id (negative value) over to the Entity object, so every entity I added to the context had an Id of zero, and this is why it was blowing up.

    Once I copied the ids over, it started working.

    • Marked as answer by zenocon Friday, May 25, 2012 1:45 PM
    Friday, May 25, 2012 1:45 PM

All replies

  • Solved.  In my Insert methods, I had forgotten to copy the client-generated sequence id (negative value) over to the Entity object, so every entity I added to the context had an Id of zero, and this is why it was blowing up.

    Once I copied the ids over, it started working.

    • Marked as answer by zenocon Friday, May 25, 2012 1:45 PM
    Friday, May 25, 2012 1:45 PM
  • Hi zenocon,

    Welcome to MSDN Forum.

    I'm glad to hear that you have solved the issue and thanks for your sharing.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us

    Monday, May 28, 2012 3:36 AM
    Moderator