试图保存我的实体时,我收到以下异常:EF 4.1代码第一次 - 复制的对象图形实体导致异常
“的AcceptChanges不能继续,因为该对象的键值与在ObjectStateManager另一个对象发生冲突。在调用AcceptChanges之前确保键值是唯一的。“
我创建了一个3层应用程序,其中数据访问层使用EF Code First,以及客户端使用WCF调用中间层。因此,我无法让上下文在客户端上建立实体时跟踪实体状态。
在某些情况下,我发现相同的实体在对象图中包含两次。在这种情况下,当我尝试设置副本的实体状态时,它会失败。
例如,我有以下实体: 客户 国家 Curreny
- 从我创建了一个客户的新 实例的客户。然后,我拨打 服务电话获取国家/地区 实例并将其分配给客户。 Country实例具有 关联的货币。
- 然后,用户可以将货币与客户关联起来。他们 可能会选择与国家相关的同一货币 。
- 我拨打另一个服务电话获得 这个。因此在这个阶段,我们可能有 有两个不同的 相同货币的实例。
所以我最终得到的是对象图中同一个实体的两个实例。
然后保存实体(在我的服务)我需要告诉EF两个货币实体没有修改(如果我不这样做,我得到重复)。问题是我得到上面的例外。
保存如果我将Country实例上的Currency实例设置为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();
}
}
}
+1提供了一个完整的例子来重现你的问题,准备好复制 - 粘贴 - 编译 - 运行!你显然花费了大量的工作来问这个问题。 – Slauma 2011-06-08 20:30:20