EF4 - POCO - SaveChanges неожиданно дублирует элементы в таблице поиска

Я использую EF4 с POCO, и приведенный ниже код - это то, что мне нужно для обновления лицензии. Я только вставил сюда часть кода, имеющую отношение к проблеме, которая добавляет LicenseDetails.

Проблема в том, что для каждого вставленного LicenseDetail EF также неожиданно добавляет строки в таблицу поиска «Item». Почему?!

Взаимосвязь обеих таблиц, определенных в базе данных (SQLServer 2008), показана ниже, и я сначала делаю базу данных, поэтому EF генерирует взаимосвязь сущностей на основе этого.

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  

метод обновления:

    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();
        }
    }

person Donald    schedule 28.10.2010    source источник


Ответы (2)


Когда вы добавляете новый LicenseDetails в контекст, вы также добавляете элементы, на которые ссылаются эти LicenseDetails. Поскольку контекст не знает, что Items уже существует в базе данных, он добавляет их. Вам необходимо сообщить контексту, что элементы уже находятся в базе данных, вызвав context.Items.Attach (licenseDetail.Item).

Вы также можете попробовать использовать

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

вместо

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

и нет необходимости использовать список toAdd вообще - просто продолжайте добавлять licenseDetails в первый цикл foreach.

person Jakub Konecki    schedule 28.10.2010
comment
Якуб, большое спасибо за помощь. Теперь я понимаю! И спасибо за дополнительные советы! - person Donald; 29.10.2010
comment
@ user428774 - Добро пожаловать! Должен признать, это немного неинтуитивно, особенно когда вы используете мир, менее ориентированный на сущность. - person Jakub Konecki; 29.10.2010
comment
Якуб, вчера я был дома, когда ответил на ваш ответ и пометил ваш ответ как принятый, потому что я вам поверил. Однако сейчас, когда я в офисе и тестирую ваше решение, у меня все еще та же проблема. - person Donald; 29.10.2010

В итоге мне пришлось выдавать context.ObjectStateManager.ChangeObjectState (d.Item, EntityState.Unchanged) для каждого добавленного LicenseDetail. Это решило проблему.

person Donald    schedule 29.10.2010