views:

24

answers:

2

I'm using EF4 with POCO and the code below is what I have to update a license. I only pasted here the section of the code that is relevant to the problem, which is adding LicenseDetails.

The problem is that for each LicenseDetail that are inserted, EF also unexpectadly adds rows to the "Item" lookup table. Why?!

The relationship of both tables defined in the database (SQLServer 2008) is shown below and I do Database first, therefore EF generates the entities' relationship based on this.

ALTER TABLE [dbo].[LicenseDetail]  
WITH CHECK ADD  CONSTRAINT [FK_LicenseDetail_Item] FOREIGN KEY([ItemId])
REFERENCES [dbo].[Item] ([Id])
GO

ALTER TABLE [dbo].[LicenseDetail] CHECK CONSTRAINT [FK_LicenseDetail_Item]
GO  

the update method:

    public static void UpdateLicense(License license)
    {
        using (ALISEntities context = new ALISEntities())
        {
            context.ContextOptions.ProxyCreationEnabled = true;

            var storedLicense = 
                context.Licenses.Include("LicenseDetails")
                .Where(o => o.Id == license.Id).SingleOrDefault();

            //////////////////////////////////////////////////////////////
            // license details to add
            List<LicenseDetail> toAdd = new List<LicenseDetail>();

            foreach (LicenseDetail ld in license.LicenseDetails)
            {
                if (storedLicense.LicenseDetails
                    .Where(d => d.ItemId == ld.ItemId).Count() == 0)
                {
                    toAdd.Add(ld);
                }
            }

            toAdd.ForEach(i => storedLicense.LicenseDetails.Add(i));

            context.SaveChanges();
        }
    }
A: 

When you add a new LicenseDetails to context you also add Items which are referenced by those LicenseDetails. Because context doesn't know that Items already exists in the database it adds them. You need to tell context that Items are already in the database by calling context.Items.Attach(licenseDetail.Item).

You might also try using

context.Licenses.Include("LicenseDetails").Find(license.Id);

instead of

context.Licenses.Include("LicenseDetails")
                .Where(o => o.Id == license.Id).SingleOrDefault();

and there is no need to use toAdd list at all - just keep adding licenseDetails in the first foreach loop.

Jakub Konecki
Jakub, thank you so much for the help. Now I understand! And thanks for the extra tips too!
Donald
@user428774 - You're welcome! It is a bit unintuitive, I must admit, especially when you cone from less 'entity-oriented' world
Jakub Konecki
Jakub, I was at home yesterday when I reply to your response and marked your answer as accepted answer because I believed you. However, now being at the office and testing your solution, I still have the same problem.
Donald
@Donald - sorry it didn't work...
Jakub Konecki
A: 

I ended up having to issue context.ObjectStateManager.ChangeObjectState(d.Item, EntityState.Unchanged) for each LicenseDetail added. That solved the problem.

Donald