我在Entity Framework Core 1.1中有以下实体和上下文:
public class Language {
public String LanguageCode { get; set; }
public virtual ICollection<LanguageI18N> LanguagesI18N { get; set; } = new List<LanguageI18N>();
}
public class LanguageI18N {
public String LanguageCode { get; set; }
public String TranslationCode { get; set; }
public String Name { get; set; }
public virtual Language Language { get; set; }
}
public class Context : DbContext {
public DbSet<Language> Languages { get; set; }
public DbSet<LanguageI18N> LanguagesI18N { get; set; }
public Context(DbContextOptions options) : base(options) { }
protected override void OnModelCreating(ModelBuilder builder) {
base.OnModelCreating(builder);
builder.Entity<Language>(b => {
b.ToTable("Languages");
b.HasKey(x => x.LanguageCode);
b.Property(x => x.LanguageCode).IsRequired(true).HasMaxLength(2).ValueGeneratedNever();
});
builder.Entity<LanguageI18N>(b => {
b.ToTable("LanguagesI18N");
b.HasKey(x => new { x.LanguageCode, x.TranslationCode });
b.Property(x => x.LanguageCode).HasMaxLength(2).IsRequired(true);
b.Property(x => x.TranslationCode).HasMaxLength(2).IsRequired(true);
b.HasOne(x => x.Language).WithMany(x => x.LanguagesI18N).HasForeignKey(x => x.LanguageCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);
b.HasOne(x => x.Language).WithMany(x => x.LanguagesI18N).HasForeignKey(x => x.TranslationCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);
});
}
}
然后我创建了一个包含翻译名称的语言,如下所示:
public class Program {
public static void Main(String[] args) {
DbContextOptionsBuilder builder = new DbContextOptionsBuilder<Context>();
builder.UseInMemoryDatabase();
using (Context context = new Context(builder.Options)) {
Language language = new Language { LanguageCode = "en" };
language.LanguagesI18N.Add(new LanguageI18N { LanguageCode = "en", TranslationCode = "en", Name = "English" });
language.LanguagesI18N.Add(new LanguageI18N { LanguageCode = "en", TranslationCode = "pt", Name = "Inglês" });
context.Languages.Add(language);
context.SaveChanges();
}
}
}
我正在使用代码“en”创建一种语言,并使用英语和葡萄牙语翻译其名称。
当我运行代码时,我收到以下错误:
未处理的异常:System.InvalidOperationException: 无法跟踪实体类型“LanguageI18N”的实例,因为已经跟踪了具有相同密钥的此类型的另一个实例。 添加新实体时,对于大多数密钥类型,如果未设置密钥,则将创建唯一的临时密钥值(即,如果为密钥属性指定了其类型的默认值)。 如果要明确设置新实体的键值,请确保它们不会与现有实体或为其他新实体生成的临时值发生冲突。
附加现有实体时,请确保只有一个具有给定键值的实体实例附加到上下文。
我错过了什么?
更新
该代码适用于InMemoryDatabase和Ivan Stoev建议的更改:
b.HasOne<Language>().WithMany().HasForeignKey(x => x.TranslationCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);
但是当我更换InMemoryDatabase时:
builder.UseInMemoryDatabase();
通过SQL Server数据库:
builder.UseSqlServer(yourConnectionString);
我收到以下错误:
Unhandled Exception: Microsoft.EntityFrameworkCore.DbUpdateException:
An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_LanguagesI18N_Languages_TranslationCode".
The conflict occurred in database "TestDb", table "dbo.Languages", column 'LanguageCode'.
The statement has been terminated.
知道为什么吗?
答案 0 :(得分:2)
下面
b.HasOne(x => x.Language).WithMany(x => x.LanguagesI18N).HasForeignKey(x => x.LanguageCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);
b.HasOne(x => x.Language).WithMany(x => x.LanguagesI18N).HasForeignKey(x => x.TranslationCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);
您正试图映射由Language
- >表示的同一个关联。导航属性LanguagesI18N
导航到两个不同的FK(LanguageCode
和TranslationCode
),这是不可能的。第二行有效地覆盖第一行,导致错误的设置。
由于您有两个one-to-many
关系,并假设第一行包含第一行的正确设置,第二行可以通过将最后一行更改为:
b.HasOne<Language>().WithMany().HasForeignKey(x => x.TranslationCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);
这种设置(两侧没有导航属性)在EF6中是不可能的,但由于HasOne
/ HasMany
方法的无参数重载,EF Core完全支持。只需将它们(以及WithOne
/ WithMany
)与模型导航属性保持同步。