C# 实体框架:附加类型为'的实体;国家';失败,因为相同类型的另一个实体已具有相同的主键值
我试图简化一个更复杂的数据模型,但为了清晰起见,让我们假设我有两个DB表:C# 实体框架:附加类型为'的实体;国家';失败,因为相同类型的另一个实体已具有相同的主键值,c#,sql,.net,sql-server,entity-framework,C#,Sql,.net,Sql Server,Entity Framework,我试图简化一个更复杂的数据模型,但为了清晰起见,让我们假设我有两个DB表:tb\u Item和tb\u Country,在tb\u Item表关联tb\u Country.CountryId和tb\u Item.MarketId 在代码中,我有Item和Country类,其中Country是Item的一个成员,因为我需要访问业务逻辑中的一些Country属性(但不应该与此问题相关) 当我从DB中检索要在UI中显示的项目记录时,我会执行以下操作(同样,简化): 其中FindItems是: publ
tb\u Item
和tb\u Country
,在tb\u Item
表关联tb\u Country.CountryId
和tb\u Item.MarketId
在代码中,我有Item
和Country
类,其中Country
是Item
的一个成员,因为我需要访问业务逻辑中的一些Country
属性(但不应该与此问题相关)
当我从DB中检索要在UI中显示的项目记录时,我会执行以下操作(同样,简化):
其中FindItems
是:
public IList<Item> FindItems(DateTime asOfDate, int? countryId)
{
using (var ctx = GetContext())
{
var rawItems = ctx.Items.Include(x => x.Country).AsQueryable();
var items = rawItems.Where(d => d.AsOfDate == rawItems.Where(d2 =>
d2.Country.IsoCountryCode == d.Country.IsoCountryCode &&
d2.AsOfDate <= asOfDate).Max(to2 => to2.AsOfDate));
if (countryId.HasValue)
items = items.Where(d => d.Country.CountryId == countryId.Value);
return items.ToList();
}
}
public Item GetItemById(long id)
{
using (var ctx = GetContext())
{
return ctx.Items.SingleOrDefault(c => c.ItemId == id);
}
}
public void SaveItem(Context ctx, Item item)
{
ctx.Items.Attach(item);
ctx.Entry(item).State = EntityState.Modified;
ctx.Items.AddOrUpdate(item);
ctx.SaveChanges();
}
…和SaveItem
是:
public IList<Item> FindItems(DateTime asOfDate, int? countryId)
{
using (var ctx = GetContext())
{
var rawItems = ctx.Items.Include(x => x.Country).AsQueryable();
var items = rawItems.Where(d => d.AsOfDate == rawItems.Where(d2 =>
d2.Country.IsoCountryCode == d.Country.IsoCountryCode &&
d2.AsOfDate <= asOfDate).Max(to2 => to2.AsOfDate));
if (countryId.HasValue)
items = items.Where(d => d.Country.CountryId == countryId.Value);
return items.ToList();
}
}
public Item GetItemById(long id)
{
using (var ctx = GetContext())
{
return ctx.Items.SingleOrDefault(c => c.ItemId == id);
}
}
public void SaveItem(Context ctx, Item item)
{
ctx.Items.Attach(item);
ctx.Entry(item).State = EntityState.Modified;
ctx.Items.AddOrUpdate(item);
ctx.SaveChanges();
}
在执行ctx.SaveChanges()
时,我正在保存一个带有CountryId
的项目记录,该记录已经存在于Country
表中,我得到了可怕的EF错误:
System.ServiceModel.FaultException:“附加类型为“MyService.Data.Model.Country”的实体失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“Unchanged”或“Modified”时可能会发生这种情况。这可能是因为某些实体是新的,尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“添加”实体状态跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”
我的问题是,我不希望在重复国家的tb_项目
表上出现唯一性约束问题(我希望项目与国家的关系是多:一)。在将记录保存到tbu项目
表时,代码几乎就好像试图将重复的国家/地区记录保存到tbu国家/地区
表
我肯定我在做一些愚蠢的事情,我就是搞不懂它是什么……我尝试了其他相关问题中的建议:尝试分离
Country
模型,在检索记录时使用AsNoTracking()
,在检索项目记录时不使用。包括(x=>x.Country)
,等等。,但这一切都不管用。有多种方法可以解释为什么会发生这种情况,但有一件事我看得很清楚,那就是为什么在连接实体之后调用AddOrUpdate?这只会再次尝试附加它,并会导致异常。请参见删除.attach或.AddOrUpdate行不会有任何区别,相同的错误。如果删除.Attach和.State行,则可以看到SQL Server错误:System.Data.Entity.Infrastructure.DbUpdateException:更新条目时出错。有关详细信息,请参见内部异常。-->System.Data.Entity.Core.UpdateException:更新条目时出错。有关详细信息,请参见内部异常。-->System.Data.SqlClient.SqlException:违反唯一密钥约束“UQ\U tb\U国家代码”。无法在对象“dbo.tb_Country”中插入重复键。
根据错误消息,保存时不应发生此情况,但尝试将项
附加到上下文时应发生此情况。如果没有更多的代码,很难说为什么会发生这种情况,但要点应该是您已经在某个地方附加了(例如)Id为1的国家(可能是因为包含调用)。然后,您还有另一个Id为1的对象引用,并尝试附加该对象引用,从而导致错误。快速而肮脏的修复方法是将FK添加到模型中,并且只设置CountryId。否则,请检查您的代码/EF调用和DbContext实例使用情况。@Jejuni所说的“将FK添加到模型”是指使用[ForeignKey(“MarketId”)]
属性装饰Item.Country属性吗?我之前尝试过,但没有运气。假设我有3个项目,其中2个项目具有相同的子国家(Id 1)。我不认为这是一个问题,因为许多物品可以有相同的国家/一个国家可以属于许多物品。我没想到会爆炸。如果我在原始SQL中简单插入一个项目,我可以使用相同的国家Id添加任意多的项目行。