c# - "Attaching an entity of type T failed because another entity of the same type already has the same primary key value"

标签 c# entity-framework entity-framework-6

我有一个 Language模型定义如下:

public class Language
{
    [JsonProperty("iso_639_1")]
    public string Iso { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        if (!(obj is Language))
        {
            return false;
        }

        return ((Language)obj).Iso == Iso;
    }

    public override int GetHashCode()
    {
        return Iso.GetHashCode();
    }
}

这用于模型 Movie作为ICollection<Language> SpokenLanguages .我正在使用收集到的信息为我的数据库播种。当多部电影使用相同的语言时,我显然想重新使用 Languages 中的现有条目。表。

以下通过重新使用现有类型并添加新类型来实现:

var localLanguages = context.Languages.ToList();
var existingLanguages = localLanguages.Union(movie.SpokenLanguages);
var newLanguages = localLanguages.Except(existingLanguages).ToList();
newLanguages.AddRange(existingLanguages);
movie.SpokenLanguages = newLanguages;

这行得通,但显然这很丑陋,而且对 EF 不友好。我正在研究将现有模型附加到 EF 并让它自动重新使用它,但我似乎无法让它工作——我最终收到此错误消息:

Attaching an entity of type 'Models.Movies.Language' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

有问题的代码是这样的:

var localLanguages = context.Languages.ToList();
foreach (var language in movie.SpokenLanguages)
{
    if (localLanguages.Contains(language))
    {
        context.Languages.Attach(language);
        // no difference between both approaches
        context.Entry(language).State = EntityState.Unchanged;
    }
}

设置状态为UnchangedModified没有区别。我收到的 JSON 响应是

{
    "iso_639_1": "en",
    "name": "English"
}

这些值与数据库中存在的值完全相同,都是字段。

数据库中的每次插入都会创建一个新的上下文并将其释放。

我怎样才能让 EF 重新使用现有的语言条目,而不必自己筛选它们?

最佳答案

我已经编辑了模型,所以它现在包含一个字段 Id 并将其用作主键。其他一切,包括相等比较,都保持不变。我现在收到一条不同的错误消息,这可能会更清楚地说明这个问题:

{"The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.MovieLanguages_dbo.Languages_LanguageId". The conflict occurred in database "MoviePicker", table "dbo.Languages", column 'Id'. The statement has been terminated."}

Additional information: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.

我在数据上下文中记录了 SQL 语句,这是最后执行的语句:

INSERT [dbo].[MovieLanguages]([MovieId], [LanguageId])
VALUES (@0, @1)

-- @0: '2' (Type = Int32)  
-- @1: '0' (Type = Int32)

这说明LanguageId(表Language中的字段Id)没有填写,这是有道理的,因为默认为0我所做的就是将它附加到 EF 配置。这不会使其假定已存在对象的值,从而导致 FK 约束错误,因为它正在尝试创建对不存在的 ID 为 0 的条目的引用。

知道这一点后,我开始结合我所拥有的和我的目标。首先,我查看该语言是否已经在数据库中。如果不是,一切都会保持正常,我只需将其插入即可。如果它已经存在,我将其 ID 分配给新的 Language 对象,分离现有对象并附加新对象。

基本上我交换了 EF 跟踪的对象。如果它在注意到对象相等时自行执行此操作,那将会非常有帮助,但在它执行此操作之前,这是我想到的最好的方法。

var localLanguages = _context.Languages.ToList();
foreach (var language in movie.SpokenLanguages)
{
    var localLanguage = localLanguages.Find(x => x.Iso == language.Iso);
    
    if (localLanguage != null)
    {
        language.Id = localLanguage.Id;
        _context.Entry(localLanguage).State = EntityState.Detached;
        _context.Languages.Attach(language);
    }
}

关于c# - "Attaching an entity of type T failed because another entity of the same type already has the same primary key value",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28545146/

相关文章:

c# - CaSTLe.Windsor 3.1 - 组件激活器 : could not proxy <FactoryName>

c# - Entity Framework 中的 Linq 查询错误

vb.net - Entity Framework 多对多使用 VB.Net Lambda

c# - 使用 Entity Framework 从动态/编程命名的列名称中获取字段

c# - Avalondock MVVM 布局

C#:从全局化/ISO 8601 兼容的 2 个日期中获取所有周数

c# - 在特定位置抑制 RichTextBox 中的换行符

c# - 如何在 Entity Framework 6 中高效设置默认实体值,数据库优先

c# - 使用数据注释创建外键

C# Roslyn 编译 - 在动态编译的代码中使用 Nuget 包 - 编译但在运行时抛出异常(无法加载文件或程序集)