Entity Framework 是在定义多对多关系时插入实体

Entity Framework is Inserting entity when defining a many-to-many relationship

我正在尝试在 Countries 之间使用 Entity Framework (v6.0) 中的两个现有实体创建多对多关系Regions(一个国家可能属于多个地区,一个地区又包含多个国家)。而不是简单地将 CountryIDRegionID 添加到连接 table 但是它试图添加一个新的 Country 记录到 countries table,当然会导致主键在尝试添加 Country[= 时出现冲突错误46=] 到 table,即使它已经存在。 ...这是代码...

SQL 服务器 Table 定义 ...

CREATE TABLE [contact].[countries]
(
    [country_id] BIGINT NOT NULL PRIMARY KEY IDENTITY, 
    [country_name] NVARCHAR(255) NOT NULL, 
    [country_code] NVARCHAR(50) NULL, 
    [country_capital] NVARCHAR(255) NULL
)

CREATE TABLE [contact].[regions]
(
    [region_id] BIGINT NOT NULL PRIMARY KEY IDENTITY, 
    [region_name] NVARCHAR(255) NOT NULL, 
    [region_desc] NVARCHAR(MAX) NULL, 
    [region_category] NVARCHAR(255) NULL,
    [created_by]         NVARCHAR(50) NOT NULL,
    [created_date]       DATETIME NOT NULL,
    [updated_by]         NVARCHAR(50) NOT NULL,
    [updated_date]       DATETIME NOT NULL
)

CREATE TABLE [contact].[country_regions]
(
    [country_id] BIGINT NOT NULL , 
    [region_id] BIGINT NOT NULL, 
    PRIMARY KEY ([region_id], [country_id]),
    [created_by]         NVARCHAR(50) NOT NULL,
    [created_date]       DATETIME NOT NULL,
    [updated_by]         NVARCHAR(50) NOT NULL,
    [updated_date]       DATETIME NOT NULL 
    CONSTRAINT [FK_CountryRegions_ToCountries] FOREIGN KEY ([country_id]) REFERENCES [contact].[countries]([country_id]) ON UPDATE CASCADE ON DELETE CASCADE,
    CONSTRAINT [FK_CountryRegions_ToRegion] FOREIGN KEY ([region_id]) REFERENCES [contact].[regions]([region_id]) ON UPDATE CASCADE ON DELETE CASCADE
)

这是模型定义...

public class Country : BaseTimestampableModel
{
    public String Name { get; set; }

    public String Code { get; set; }

    public String Capital { get; set; }

    public virtual ICollection<State> States { get; set; }

    public virtual ICollection<Region> Regions { get; set; }
}

public class Region : BaseTimestampableModel
{
    public String Name { get; set; }

    public String Description { get; set; }

    public String Category { get; set; }

    public virtual ICollection<State> States { get; set; }

    public virtual ICollection<Country> Countries { get; set; }
}

Code-First 映射代码...

    internal static void Map(ref DbModelBuilder modelBuilder)
    {
        var entityMap = modelBuilder.Entity<Country>();
        entityMap.ToTable("countries", schemaName: "contact");

        #region map columns

        entityMap
            .HasKey(e => e.ID)
            .Property(e => e.ID)
            .HasColumnName("country_id")
            .HasColumnType("bigint")
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
            .IsRequired();

        entityMap
            .Property(e => e.Name)
            .HasColumnName("country_name")
            .HasColumnType("nvarchar")
            .HasMaxLength(255)
            .IsRequired();

        entityMap
            .Property(e => e.Code)
            .HasColumnName("country_code")
            .HasColumnType("nvarchar")
            .HasMaxLength(50)
            .IsOptional();

        entityMap
            .Property(e => e.Capital)
            .HasColumnName("country_capital")
            .HasColumnType("nvarchar")
            .HasMaxLength(255)
            .IsOptional();

        #endregion map columns

        #region navigation props

        entityMap
            .HasMany(e => e.States)
            .WithOptional(s=>s.Country);

        #endregion navigation props

        TimestampableModelMapper.Map<Country>(ref modelBuilder);
    }
}

public class RegionMapper
{
    internal static void Map(ref DbModelBuilder modelBuilder)
    {
        var entityMap = modelBuilder.Entity<Region>();
        entityMap.ToTable("regions", schemaName: "contact");

        #region map columns

        entityMap
            .HasKey(e => e.ID)
            .Property(e => e.ID)
            .HasColumnName("region_id")
            .HasColumnType("bigint")
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
            .IsRequired();

        entityMap
            .Property(e => e.Name)
            .HasColumnName("region_name")
            .HasColumnType("nvarchar")
            .HasMaxLength(255)
            .IsRequired();

        entityMap
            .Property(e => e.Description)
            .HasColumnName("region_desc")
            .HasColumnType("nvarchar")
            .IsMaxLength()
            .IsOptional();

        entityMap
            .Property(e => e.Category)
            .HasColumnName("region_category")
            .HasColumnType("nvarchar")
            .HasMaxLength(255)
            .IsOptional();

        #endregion map columns

        #region navigation props

        entityMap
            .HasMany(r => r.Countries)
            .WithMany(c => c.Regions)
            .Map(cr =>
            {
                cr.MapLeftKey("country_id");
                cr.MapRightKey("region_id");
                cr.ToTable("country_regions", schemaName: "contact");
            });

        entityMap
            .HasMany(r => r.States)
            .WithMany(s => s.Regions)
            .Map(sr =>
            {
                sr.MapLeftKey("state_id");
                sr.MapRightKey("region_id");
                sr.ToTable("state_regions", schemaName:"contact");
            });


        #endregion navigation props

        TimestampableModelMapper.Map<Region>(ref modelBuilder);
    }
}

最后是抛出错误的代码...我使用相同的上下文检索对 CountryRegion 记录的引用所以不应该有跨上下文问题,我也使用 ID 检索它们,而不是传递模型以避免附加和分离实体的问题;检索到的实例已被上下文跟踪。

Region region   = DbContext.Regions.Where(e => e.ID == regionID).SingleOrDefault();
Country country = DbContext.Countries.Where(e => e.ID == countryID).SingleOrDefault();

region.Countries.Add(country);
DbContext.SaveChanges();

这一切都以错误告终...

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_CountryRegions_ToCountries". 
The conflict occurred in database "DataBaseName", table "contact.countries", column 'country_id'.
The statement has been terminated.

当然,我并没有尝试将记录插入 "Countries" table,但是所有的映射似乎都是正确的,所以我不清楚为什么要这样做。

我已经看到这个问题发布了好几次,但很多都没有得到解答,而且发布的答案(我看到的)对我没有用。发布的大多数问题都已通过将模型附加回上下文得到解决......然而,这对我来说应该不是问题,因为我使用的是单个上下文并且已经使用 attached/tracked 个实体。

有什么想法……??感谢您的帮助...

我试过的一些建议...

// 
// set the state to unchanged ...
Region region   = DbContext.Regions.Where(e => e.ID == regionID).SingleOrDefault();
Country country = DbContext.Countries.Where(e => e.ID == countryID).SingleOrDefault();

uow.DbContext.Entry<Country>(country).State = System.Data.Entity.EntityState.Unchanged;
region.Countries.Add(country);
DbContext.SaveChanges();

//
// Add region to the country rather than country to the region
Region region   = DbContext.Regions.Where(e => e.ID == regionID).SingleOrDefault();
Country country = DbContext.Countries.Where(e => e.ID == countryID).SingleOrDefault();

country.Regions.Add(region);
DbContext.SaveChanges();

我弄清楚了问题所在...尽管有很多相反的例子...我在 RegionMapper 中反向映射了多对多关系。

原来是这样……

    #region navigation props

    entityMap
        .HasMany(r => r.Countries)
        .WithMany(c => c.Regions)
        .Map(cr =>
        {
            cr.MapLeftKey("country_id");
            cr.MapRightKey("region_id");
            cr.ToTable("country_regions", schemaName: "contact");
        });

    entityMap
        .HasMany(r => r.States)
        .WithMany(s => s.Regions)
        .Map(sr =>
        {
            sr.MapLeftKey("state_id");
            sr.MapRightKey("region_id");
            sr.ToTable("state_regions", schemaName:"contact");
        });


    #endregion navigation props

简单地切换左右键就可以了...

        #region navigation props

        entityMap
            .HasMany(r => r.Countries)
            .WithMany(c => c.Regions)
            .Map(cr =>
            {
                cr.MapLeftKey("region_id");
                cr.MapRightKey("country_id");
                cr.ToTable("country_regions", schemaName: "contact");
            });

        entityMap
            .HasMany(r => r.States)
            .WithMany(s => s.Regions)
            .Map(sr =>
            {
                sr.MapLeftKey("region_id");
                sr.MapRightKey("state_id");
                sr.ToTable("state_regions", schemaName:"contact");
            });


        #endregion navigation props

我没有详细阅读所有内容,但 this post 建议可能在 EF5 中更改了顺序,这是有道理的,因为我最初查看的大多数示例都来自 EF4 实现。