Jor*_*s L 3 c# many-to-many join entity-framework-core
如何使用连接实体(链接表)在 EF Core 5.x 中的实体之间添加多个多对多关系?
我理解的是Card和Game之间的两种多对多的关系。一种关系不是问题,但两种(或更多)关系我无法正确配置。
目前对我有用的是,我创建了两个不同的类 DeckGameCard 和 TableGameCard(连接实体),这迫使 EF 创建两个表。除了名称之外,这些类都是相同的。数据库中是否可以只有一个类(连接实体)和两个链接表?
我想要的是这样的:
public class Game
{
[Key]
public int Id { get; set; }
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
...
Run Code Online (Sandbox Code Playgroud)
但目前这(下面的代码)是我的解决方案(不是最佳的,因为 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);
}
Run Code Online (Sandbox Code Playgroud)
是的,可以使用 EF Core 5.0 引入的共享类型实体类型,但不确定它是否值得,因为对象的类型不再唯一标识实体类型,因此大多数(如果不是全部)通用和非通用实体服务(方法) ofDbContext不起作用,你必须使用相应的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; }
}
Run Code Online (Sandbox Code Playgroud)
它可以配置如下:
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);
});
Run Code Online (Sandbox Code Playgroud)
或者由于唯一的区别是实体(表)名称和相应的集合导航属性,因此可以将配置分解为类似于以下的方法
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);
}
Run Code Online (Sandbox Code Playgroud)
所以配置变得简单
GameCardEntity(modelBuilder, "DeckGameCard", c => c.Deck, g => g.Deck);
GameCardEntity(modelBuilder, "TableGameCard", c => c.OnTable, g => g.OnTable);
Run Code Online (Sandbox Code Playgroud)
这应该回答你的具体问题。但同样,请确保您了解它的潜在问题。与“直接”解决方案相比,使用基类(而不是实体)实现相同的可重用性,而没有上述缺点,例如
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 { }
Run Code Online (Sandbox Code Playgroud)
复合 PK 只需要流畅的配置
modelBuilder.Entity<DeskGameCard>().HasKey(e => new { e.GameId, e.CardId });
modelBuilder.Entity<TableGameCard>().HasKey(e => new { e.GameId, e.CardId });
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4139 次 |
| 最近记录: |