Entity Framework 4.1 - ошибка в данных отношения «многие ко многим» при вставке ранее удаленных данных - PullRequest
0 голосов
/ 13 января 2012

Сценарии:

3 таблицы, которые у меня есть, которые формируют отношение «многие ко многим»:

Agent (AgentID (PK), AgentName)
AgentChannel (AgentID (PK), ChannelID (PK))
Channel (ChannelID (PK), ChannelName)

Я не хочу добавлять / удалять записи в таблицах агентов и каналов, а только изменять таблицу AgentChannel.

Я использую EF 4.1, POCO и Self-Tracking Entity. Все коды, относящиеся к базе данных, включены в слой WCF, который я называю их кодом на стороне сервера, и я могу контролировать только добавление / удаление записей в коде на стороне клиента.

Например: У меня есть одна запись агента с несколькими записями канала. Я могу связать существующий канал с агентом следующим образом:

var channel = new Channel { ChannelID = 1 };
channel.MarkAsUnchanged();
agent.Channels.Add(channel);
// This will add new entry to AgentChannel table, but no change on Agent and Channel tables

Также я могу удалить связь канала с агентом следующим образом:

var tempChannels = agent.Channels.ToList();
var channelToDelete = tempChannels.FirstOrDefault(c => c.ChannelID == 3);
agent.Channels.Remove(channelToDelete);
// This will remove the entry in AgentChannel, but no change on Agent and Channel tables.



Моя проблема:
Если я удалил канал и добавил обратно новый канал с тем же ChannelID, что и ранее удаленный канал, в коллекцию agent.Channels, я получу эту ошибку:

AcceptChanges не может продолжаться, поскольку значения ключей объекта конфликтуют с другим объектом в ObjectStateManager.

Пример кода для демонстрации:

        //
        // I not able to call ObjectContext here because it was reside in server side.
        //

        //
        // This is client side code
        //
        var tempChannels = agent.Channels.ToList();
        var channelToDelete = tempChannels.FirstOrDefault(c => c.ChannelID == 3);

        // remove channel 3
        agent.Channels.Remove(channelToDelete);

        var channel = new Channel { ChannelID = 3 };
        channel.MarkAsUnchanged();

        // Add back channel 3
        agent.Channels.Add(channel);

        //
        // This is server side code (WCF layer)
        //
        try
        {
            using (var ctx = new testEntities())
            {
                ctx.ApplyChanges("Agents", agent); // <-- ERROR happen here
                ctx.SaveChanges();
            }
        }
        catch (Exception)
        {
            throw;
        }

тогда я получил сообщение об исключении:

    System.InvalidOperationException was unhandled
  Message=AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.
  Source=System.Data.Entity
  StackTrace:
       at System.Data.Objects.ObjectStateManager.FixupKey(EntityEntry entry)
       at System.Data.Objects.EntityEntry.AcceptChanges()
       at System.Data.Objects.EntityEntry.ChangeObjectState(EntityState requestedState)
       at System.Data.Objects.ObjectStateManager.ChangeObjectState(Object entity, EntityState entityState)
       at TestManyToMany.SelfTrackingEntitiesContextExtensions.ChangeEntityStateBasedOnObjectState(ObjectContext context, IObjectWithChangeTracker entity) in F:\MyFile\Development\Temp\TestManyToMany\TestManyToMany\Model1.Context.Extensions.cs:line 728
       at TestManyToMany.SelfTrackingEntitiesContextExtensions.HandleEntity(ObjectContext context, EntityIndex entityIndex, RelationshipSet allRelationships, IObjectWithChangeTracker entity) in F:\MyFile\Development\Temp\TestManyToMany\TestManyToMany\Model1.Context.Extensions.cs:line 596
       at TestManyToMany.SelfTrackingEntitiesContextExtensions.ApplyChanges[TEntity](ObjectContext context, String entitySetName, TEntity entity) in F:\MyFile\Development\Temp\TestManyToMany\TestManyToMany\Model1.Context.Extensions.cs:line 84
       at TestManyToMany.Program.Main(String[] args) in F:\MyFile\Development\Temp\TestManyToMany\TestManyToMany\Program.cs:line 53
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

Надеюсь, я проясню. Это единственная проблема, с которой я сталкиваюсь сейчас, и у меня заканчиваются идеи.

Большое спасибо.

-


-

Обновление - Ответ

Я получил ответ после проведенного тестирования согласно комментарию от Ладислава Мрнки.

Как правило, вам нужно добавить обратно к той же сущности, которая была удалена ранее, никакой другой сущности, потому что это даст вам ту же ошибку, что и выше.

Вот пример кода на стороне клиента, никаких изменений на стороне сервера не требуется:

        int channelId = 1;

        var tempChannels = agent.Channels.ToList();
        var channelToDelete = tempChannels.FirstOrDefault(c => c.ChannelID == channelId);

        // remove channel 1
        agent.Channels.Remove(channelToDelete);


        //var channel = _allChannels.First(c => c.ChannelID == channelId);
        //agent.Channels.Add(channel);           <-- This line will give you ERROR because the channel entity is from _allChannels, but not from agent.Channels

        // Add back channel 1
        agent.Channels.Add(channelToDelete); //  <-- This is CORRECT

1 Ответ

0 голосов
/ 13 января 2012

Это недопустимая операция.Если вы удалили канал и хотите добавить его снова, вы не должны создавать новый экземпляр.Вы должны использовать старый и только изменить его состояние.Основное правило заключается в том, что каждый объект с уникальным ключом должен быть объявлен только один раз.Ваш код приводит к двум случаям с одним и тем же ключом = ошибка.

...