Entity Framework Core Multiple 多对多链接 table(加入实体)
Entity Framework Core Multiple Many-to-many with linking table (join entity)
如何使用连接实体(链接表)在 EF Core 5.x 中的实体之间添加多个多对多关系?
what我what是Card和Game之间的两个多对多关系。一个关系不是问题,但两个(或更多)我无法正确配置它。
目前对我有用的是我创建了两个不同的 classes DeckGameCard 和 TableGameCard(连接实体),这会强制 EF 创建两个表。 class 除了名称不同外都是一样的。是否可以在数据库中只有一张class(join实体)和两张链接表?
我要的是这个:
public class Game
{
[Key]
public int Id { get; set; }
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
...
但目前这个(下面的代码)是我的解决方案(不是最优的,因为 DeckGameCard 和 TableGameCard 中的代码重复)。
public class DeckGameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
public class TableGameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
public class Game
{
[Key]
public int Id { get; set; }
public ICollection<DeckGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
[Required]
public int CardIndex { get; set; }
[Required]
public int PlayerId { get; set; }
[Required]
public Player Player { get; set; }
}
public class Card : IEntity
{
[Key]
public int Id { get; set; }
[Required]
public Shape Shape { get; set; }
[Required]
public Fill Fill { get; set; }
[Required]
public Color Color { get; set; }
[Required]
public int NrOfShapes { get; set; }
public ICollection<DeckGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<DeckGameCard>()
.HasKey(x => new {x.GameId, x.CardId});
modelBuilder.Entity<DeckGameCard>()
.HasOne(gc => gc.Card)
.WithMany(b => b.Deck)
.HasForeignKey(gc => gc.CardId);
modelBuilder.Entity<DeckGameCard>()
.HasOne(gc => gc.Game)
.WithMany(g => g.Deck)
.HasForeignKey(gc => gc.GameId);
modelBuilder.Entity<TableGameCard>()
.HasKey(x => new {x.GameId, x.CardId});
modelBuilder.Entity<TableGameCard>()
.HasOne(gc => gc.Card)
.WithMany(b => b.OnTable)
.HasForeignKey(bc => bc.CardId);
modelBuilder.Entity<TableGameCard>()
.HasOne(gc => gc.Game)
.WithMany(g => g.OnTable)
.HasForeignKey(gc => gc.GameId);
}
是的,可以通过使用引入的 EF Core 5.0 Shared-type entity types,但不确定它是否值得,因为对象的类型不再唯一标识实体类型,所以大多数(如果不是全部)通用和非通用DbContext
的实体服务(方法)将不起作用,您必须使用相应的 DbSet<T>
方法,使用 Set<T>(name)
重载获取它。并且没有等效的 Entry
方法,因此在断开连接的情况下更改跟踪可能会遇到问题。
话虽如此,下面是如何在模型级别完成的。
给出的模型类似于:
public class Game
{
public int Id { get; set; }
// ...
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
}
public class Card
{
public int Id { get; set; }
// ...
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
}
public class GameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
可配置如下:
modelBuilder.SharedTypeEntity<GameCard>("DeckGameCard", builder =>
{
builder.ToTable("DeckGameCard");
builder.HasKey(e => new { e.GameId, e.CardId });
builder.HasOne(e => e.Card).WithMany(e => e.Deck);
builder.HasOne(e => e.Game).WithMany(e => e.Deck);
});
modelBuilder.SharedTypeEntity<GameCard>("TableGameCard", builder =>
{
builder.ToTable("TableGameCard");
builder.HasKey(e => new { e.GameId, e.CardId });
builder.HasOne(e => e.Card).WithMany(e => e.OnTable);
builder.HasOne(e => e.Game).WithMany(e => e.OnTable);
});
或者因为唯一的区别是实体(table)名称和相应的集合导航属性,配置可以分解为类似于
的方法
static void GameCardEntity(
ModelBuilder modelBuilder, string name,
Expression<Func<Card, IEnumerable<GameCard>>> cardCollection,
Expression<Func<Game, IEnumerable<GameCard>>> gameCollection,
string tableName = null
)
{
var builder = modelBuilder.SharedTypeEntity<GameCard>(name);
builder.ToTable(tableName ?? name);
builder.HasKey(e => new { e.GameId, e.CardId });
builder.HasOne(e => e.Card).WithMany(cardCollection);
builder.HasOne(e => e.Game).WithMany(gameCollection);
}
所以配置变得简单
GameCardEntity(modelBuilder, "DeckGameCard", c => c.Deck, g => g.Deck);
GameCardEntity(modelBuilder, "TableGameCard", c => c.OnTable, g => g.OnTable);
这应该可以回答您的具体问题。但同样,请确保您了解它的潜在问题。并与实现相同可重用性的“直接”解决方案相比,基础 class(不是 entity)没有上述缺点,例如
public class Game
{
public int Id { get; set; }
// ...
public ICollection<DeskGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
}
public class Card
{
public int Id { get; set; }
// ...
public ICollection<DeskGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
}
public abstract class GameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
public class DeskGameCard : GameCard { }
public class TableGameCard : GameCard { }
复合PK只需要流畅的配置
modelBuilder.Entity<DeskGameCard>().HasKey(e => new { e.GameId, e.CardId });
modelBuilder.Entity<TableGameCard>().HasKey(e => new { e.GameId, e.CardId });
如何使用连接实体(链接表)在 EF Core 5.x 中的实体之间添加多个多对多关系?
what我what是Card和Game之间的两个多对多关系。一个关系不是问题,但两个(或更多)我无法正确配置它。
目前对我有用的是我创建了两个不同的 classes DeckGameCard 和 TableGameCard(连接实体),这会强制 EF 创建两个表。 class 除了名称不同外都是一样的。是否可以在数据库中只有一张class(join实体)和两张链接表?
我要的是这个:
public class Game
{
[Key]
public int Id { get; set; }
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
...
但目前这个(下面的代码)是我的解决方案(不是最优的,因为 DeckGameCard 和 TableGameCard 中的代码重复)。
public class DeckGameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
public class TableGameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
public class Game
{
[Key]
public int Id { get; set; }
public ICollection<DeckGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
[Required]
public int CardIndex { get; set; }
[Required]
public int PlayerId { get; set; }
[Required]
public Player Player { get; set; }
}
public class Card : IEntity
{
[Key]
public int Id { get; set; }
[Required]
public Shape Shape { get; set; }
[Required]
public Fill Fill { get; set; }
[Required]
public Color Color { get; set; }
[Required]
public int NrOfShapes { get; set; }
public ICollection<DeckGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<DeckGameCard>()
.HasKey(x => new {x.GameId, x.CardId});
modelBuilder.Entity<DeckGameCard>()
.HasOne(gc => gc.Card)
.WithMany(b => b.Deck)
.HasForeignKey(gc => gc.CardId);
modelBuilder.Entity<DeckGameCard>()
.HasOne(gc => gc.Game)
.WithMany(g => g.Deck)
.HasForeignKey(gc => gc.GameId);
modelBuilder.Entity<TableGameCard>()
.HasKey(x => new {x.GameId, x.CardId});
modelBuilder.Entity<TableGameCard>()
.HasOne(gc => gc.Card)
.WithMany(b => b.OnTable)
.HasForeignKey(bc => bc.CardId);
modelBuilder.Entity<TableGameCard>()
.HasOne(gc => gc.Game)
.WithMany(g => g.OnTable)
.HasForeignKey(gc => gc.GameId);
}
是的,可以通过使用引入的 EF Core 5.0 Shared-type entity types,但不确定它是否值得,因为对象的类型不再唯一标识实体类型,所以大多数(如果不是全部)通用和非通用DbContext
的实体服务(方法)将不起作用,您必须使用相应的 DbSet<T>
方法,使用 Set<T>(name)
重载获取它。并且没有等效的 Entry
方法,因此在断开连接的情况下更改跟踪可能会遇到问题。
话虽如此,下面是如何在模型级别完成的。
给出的模型类似于:
public class Game
{
public int Id { get; set; }
// ...
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
}
public class Card
{
public int Id { get; set; }
// ...
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
}
public class GameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
可配置如下:
modelBuilder.SharedTypeEntity<GameCard>("DeckGameCard", builder =>
{
builder.ToTable("DeckGameCard");
builder.HasKey(e => new { e.GameId, e.CardId });
builder.HasOne(e => e.Card).WithMany(e => e.Deck);
builder.HasOne(e => e.Game).WithMany(e => e.Deck);
});
modelBuilder.SharedTypeEntity<GameCard>("TableGameCard", builder =>
{
builder.ToTable("TableGameCard");
builder.HasKey(e => new { e.GameId, e.CardId });
builder.HasOne(e => e.Card).WithMany(e => e.OnTable);
builder.HasOne(e => e.Game).WithMany(e => e.OnTable);
});
或者因为唯一的区别是实体(table)名称和相应的集合导航属性,配置可以分解为类似于
的方法
static void GameCardEntity(
ModelBuilder modelBuilder, string name,
Expression<Func<Card, IEnumerable<GameCard>>> cardCollection,
Expression<Func<Game, IEnumerable<GameCard>>> gameCollection,
string tableName = null
)
{
var builder = modelBuilder.SharedTypeEntity<GameCard>(name);
builder.ToTable(tableName ?? name);
builder.HasKey(e => new { e.GameId, e.CardId });
builder.HasOne(e => e.Card).WithMany(cardCollection);
builder.HasOne(e => e.Game).WithMany(gameCollection);
}
所以配置变得简单
GameCardEntity(modelBuilder, "DeckGameCard", c => c.Deck, g => g.Deck);
GameCardEntity(modelBuilder, "TableGameCard", c => c.OnTable, g => g.OnTable);
这应该可以回答您的具体问题。但同样,请确保您了解它的潜在问题。并与实现相同可重用性的“直接”解决方案相比,基础 class(不是 entity)没有上述缺点,例如
public class Game
{
public int Id { get; set; }
// ...
public ICollection<DeskGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
}
public class Card
{
public int Id { get; set; }
// ...
public ICollection<DeskGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
}
public abstract class GameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
public class DeskGameCard : GameCard { }
public class TableGameCard : GameCard { }
复合PK只需要流畅的配置
modelBuilder.Entity<DeskGameCard>().HasKey(e => new { e.GameId, e.CardId });
modelBuilder.Entity<TableGameCard>().HasKey(e => new { e.GameId, e.CardId });