我正在将一个项目从NHibernate迁移到EF,我刚刚开始使用EF7。因为它是新的,所以似乎没有很多关于这方面的帖子。我在旧的EF版本上找到了几个,但它们不适用/工作。我有一个艰难的时间得到多对多关系配置使用相交表我在逐字逐句地使用这个例子,我得到一个运行时错误,说我的对象的属性没有外键。
我的对象如下:
public partial class Casino
{
// other properties
private IList<Country> _prohibitedCountries;
public IList<Country> ProhibitedCountries
{
get { return _prohibitedCountries; }
protected set { _prohibitedCountries = value; }
}
}
public partial class Country
{
// other properties
private IList<Casino> _prohibitedCasinos;
public IList<Casino> ProhibitedCasinos
{
get { return _prohibitedCasinos; }
protected set { _prohibitedCasinos = value; }
}
}
所以一个赌场可以有很多被禁止的国家,反之亦然。
My DB Context定义如下:
public class DsDbContext : DbContext
{
public DbSet<Casino> Casinos { get; set; }
public DbSet<Country> Countries { get; set; }
// Other collections...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString = configuration.GetConnectionString("DataStoreDatabase");
optionsBuilder.UseSqlServer(connectionString);
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Casino>()
.HasMany(a => a.ProhibitedCountries)
.WithMany(b => b.ProhibitedCasinos)
.UsingEntity<Dictionary<string, object>>(
"Casino_Prohibited_Country",
j => j
.HasOne<Country>()
.WithMany()
.HasForeignKey("CountryId")
.HasConstraintName("FK_CasinoProhibitedCountry_Country")
.OnDelete(DeleteBehavior.Cascade) ,
j => j
.HasOne<Casino>()
.WithMany()
.HasForeignKey("CasinoId")
.HasConstraintName("FK_CasinoProhibitedCountry_Casino")
.OnDelete(DeleteBehavior.Cascade)
);
modelBuilder
.Entity<Country>()
.HasMany(c => c.ProhibitedCasinos)
.WithMany(c => c.ProhibitedCountries)
.UsingEntity<Dictionary<string, object>>(
"Casino_Prohibited_Country",
j => j
.HasOne<Casino>()
.WithMany()
.HasForeignKey("CasinoId")
.HasConstraintName("FK_CasinoProhibitedCountry_Casino")
.OnDelete(DeleteBehavior.Cascade),
j => j
.HasOne<Country>()
.WithMany()
.HasForeignKey("CountryId")
.HasConstraintName("FK_CasinoProhibitedCountry_Country")
.OnDelete(DeleteBehavior.Cascade)
);
}
,这是显示约束和fk的相交表的脚本。我不希望有一个实体对应于这样的每个相交表。我确实尝试了这个配置,得到了完全相同的运行时错误
CREATE TABLE [dbo].[Casino_Prohibited_Country](
[CasinoId] [int] NOT NULL,
[CountryId] [int] NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Casino_Prohibited_Country] WITH NOCHECK ADD CONSTRAINT [FK_CasinoProhibitedCountry_Casino] FOREIGN KEY([CasinoId])
REFERENCES [dbo].[Casino] ([Id])
GO
ALTER TABLE [dbo].[Casino_Prohibited_Country] CHECK CONSTRAINT [FK_CasinoProhibitedCountry_Casino]
GO
ALTER TABLE [dbo].[Casino_Prohibited_Country] WITH NOCHECK ADD CONSTRAINT [FK_CasinoProhibitedCountry_Country] FOREIGN KEY([CountryId])
REFERENCES [dbo].[Country] ([Id])
GO
ALTER TABLE [dbo].[Casino_Prohibited_Country] CHECK CONSTRAINT [FK_CasinoProhibitedCountry_Country]
GO
当我执行单元测试(只做一些基本的CRUD工作)时,我在基础存储库的构造函数中得到一个运行时错误
public BaseRepository(DsDbContext context)
{
_context = context;
_dbSet = context.Set<TEntity>(); // The error below is thrown on this line
}
System.InvalidOperationException: 'The skip navigation 'Country.ProhibitedCasinos' doesn't have a foreign key associated with it. Every skip navigation must have a configured foreign key.'
我不明白为什么EF在配置中显式声明时找不到外键。EF能够访问DB并读取没有关系的实体的记录
我在EFCore github repo上从微软工程师那里找到了一个(非常反直觉的)解决方案
实际上,我需要做的是删除这个:.HasConstraintName("FK_CasinoProhibitedCountry_Country")
当错误信息为 时,是没有意义的
System.InvalidOperationException: 'The skip navigation 'Country.ProhibitedCasinos' doesn't have a foreign key associated with it. Every skip navigation must have a configured foreign key.'
另一个变化与伊万·斯托夫在他的评论中建议的一致:删除第二个定义。
下面是新的/working配置。注意MS文档中缺少的HasConstraintName
设置。另外,请注意,这只在Casino实体上配置,而不是在Country实体上进行反向配置。
modelBuilder.Entity<Casino>()
.HasMany(e => e.ProhibitedCountries)
.WithMany(e => e.ProhibitedCasinos)
.UsingEntity<Dictionary<string, object>>(
"Casino_Prohibited_Country",
e => e
.HasOne<Country>()
.WithMany()
.HasForeignKey("CountryId"),
e => e
.HasOne<Casino>()
.WithMany()
.HasForeignKey("CasinoId")
);
git上的MS工程师建议从Casino_Prohibited_Country
相交表的两列制作复合主键,但我没有这样做,它仍然有效,所以这似乎不是必要的