EF 4.1 Code First - повторяющиеся объекты в графе объектов вызывают исключение

Я получаю следующее исключение при попытке сохранить мою сущность:

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

Я создаю трехуровневое приложение, в котором уровень доступа к данным использует EF Code First,и где клиент вызывает средний уровень с помощью WCF. Поэтому я не могу позволить контексту отслеживать состояние сущности при создании сущности на клиенте.

В некоторых ситуациях я обнаруживаю, что одна и та же сущность содержится дважды в графе объектов. В этой ситуации это не удается, когда я пытаюсь установить состояние объекта дубликата.

Например, у меня есть следующие объекты: Покупатель Страна Curreny

  1. Из клиента создаю новый экземпляр клиента. Затем я делаю вызов службы, чтобы получить страну экземпляр и передать его Заказчику. Экземпляр Country имеет связанная валюта.
  2. Затем пользователь может связать Валюта у покупателя. Oни вполне можете выбрать ту же валюту это связано со страной.
  3. Я звоню еще раз, чтобы получить этот. Таким образом, на данном этапе мы можем иметь два отдельных экземпляра одна и та же валюта.

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

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

При сохранении, если я устанавливаю для экземпляра Currency в экземпляре Country значение null, это решает проблему, но я чувствую, что код становится все более беспорядочным (из-за этого и других связанных с WCF EF обходные пути, которые я должен использовать).

Есть ли какие-нибудь предложения, как решить эту проблему более приятным способом?

Большое спасибо за любую помощь заранее. Вот код:

using System;
using System.Collections.Generic;
using System.Data.Entity.ModelConfiguration;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;

namespace OneToManyWithDefault
{

    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Country Country { get; set; }
        public Currency Currency { get; set; }
        public byte[] TimeStamp { get; set; }
    }

    public class Country
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Currency Currency { get; set; }
        public byte[] TimeStamp { get; set; }
    }

    public class Currency
    {
        public int Id { get; set; }
        public string Symbol { get; set; }
        public byte[] TimeStamp { get; set; }
    }


    public class MyContext
        : DbContext
    {
        public DbSet<Customer> Customers { get; set; }
        public DbSet<Currency> Currency { get; set; }
        public DbSet<Country> Country { get; set; }

        public MyContext(string connectionString)
            : base(connectionString)
        {
            Configuration.LazyLoadingEnabled = false;
            Configuration.ProxyCreationEnabled = false;
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new CustomerConfiguration());
            modelBuilder.Configurations.Add(new CountryConfiguration());
            modelBuilder.Configurations.Add(new CurrencyConfiguration());
            base.OnModelCreating(modelBuilder);
        }
    }

    public class CustomerConfiguration
        : EntityTypeConfiguration<Customer>
    {
        public CustomerConfiguration()
            : base()
        {
            HasKey(p => p.Id);
            Property(p => p.Id)
                .HasColumnName("Id")
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();
            Property(p => p.TimeStamp)
                .HasColumnName("TimeStamp")
                .IsRowVersion();

            ToTable("Customers");
        }
    }

    public class CountryConfiguration
        : EntityTypeConfiguration<Country>
    {
        public CountryConfiguration()
            : base()
        {
            HasKey(p => p.Id);
            Property(p => p.Id)
                .HasColumnName("Id")
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();
            Property(p => p.TimeStamp)
                .HasColumnName("TimeStamp")
                .IsRowVersion();

            ToTable("Countries");
        }
    }

    public class CurrencyConfiguration
        : EntityTypeConfiguration<Currency>
    {
        public CurrencyConfiguration()
            : base()
        {
            HasKey(p => p.Id);
            Property(p => p.Id)
                .HasColumnName("Id")
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();
            Property(p => p.TimeStamp)
                .HasColumnName("TimeStamp")
                .IsRowVersion();

            ToTable("Currencies");
        }
    }

    class Program
    {
        private const string ConnectionString =
            @"Server=.\sql2005;Database=DuplicateEntities;integrated security=SSPI;";

        static void Main(string[] args)
        {
            // Seed the database
            MyContext context1 = new MyContext(ConnectionString);

            Currency currency = new Currency();
            currency.Symbol = "GBP";
            context1.Currency.Add(currency);

            Currency currency2 = new Currency();
            currency2.Symbol = "USD";
            context1.Currency.Add(currency2);

            Country country = new Country();
            country.Name = "UK";
            country.Currency = currency;
            context1.Country.Add(country);

            context1.SaveChanges();

            // Now add a new customer
            Customer customer = new Customer();
            customer.Name = "Customer1";

            // Assign a country to the customer
            // Create a new context (to simulate making service calls over WCF)
            MyContext context2 = new MyContext(ConnectionString);
            var countries = from c in context2.Country.Include(c => c.Currency) where c.Name == "UK" select c;
            customer.Country = countries.First();

            // Assign a currency to the customer
            // Again create a new context (to simulate making service calls over WCF)
            MyContext context3 = new MyContext(ConnectionString);
            customer.Currency = context3.Currency.First(e => e.Symbol == "GBP");

            // Again create a new context (to simulate making service calls over WCF)
            MyContext context4 = new MyContext(ConnectionString);
            context4.Customers.Add(customer);

            // Uncommenting the following line prevents the exception raised below
            //customer.Country.Currency = null;

            context4.Entry(customer.Country).State = System.Data.EntityState.Unchanged;
            context4.Entry(customer.Currency).State = System.Data.EntityState.Unchanged;

            // The following line will result in this exception:
            // 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.
            context4.Entry(customer.Country.Currency).State = System.Data.EntityState.Unchanged;
            context4.SaveChanges();

            Console.WriteLine("Done.");
            Console.ReadLine();
        }
    }



}
7
задан P2l 8 June 2011 в 14:50
поделиться