EF Core - 跨 4 个表的多对多关系
EF Core - Many-To-Many Relationship across 4 Tables
考虑以下 3 个实体 A
、B
和 C
。 A
-to-C
是多对多关系。 B
-to-A
是一对多的关系(B
有很多A
,这意味着B
-to-C
也是多对多)。
如果您觉得这些过于复杂的关系过于模糊,请考虑以下示例:
- 一个
Track
(A
)有多个Artist
(C
),一个Artist
有多个Track
(很多-对多)
- 一个
Album
(B
) 有多个 Track
。 (一对多,一个Track
不能属于多个Album
)
所以(暗示):
- 一个
Album
有多个Artist
,一个Artist
有多个Album
(多对多)
问题:如何建立这种关系的实体?使用以下代码,我已经建立了Album
-to-Artist
之间的关系,Track
-到-Artist
。 如何创建 Artist
-Album
关系 因为它似乎需要加入 4 tables/entities.
public class Context: DbContext
{
// irrelevant code omitted...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TracksToArtists>()
.HasKey(r => new {r.TrackId, r.ArtistId});
modelBuilder.Entity<TracksToArtists>()
.HasOne(r => r.Track)
.WithMany(t => t.Artists)
.HasForeignKey(r => r.TrackId);
modelBuilder.Entity<TracksToArtists>()
.HasOne(r => r.Artist)
.WithMany(a => a.Tracks)
.HasForeignKey(r => r.ArtistId);
}
}
public class Track
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid AlbumId { get; set; }
public Album Album { get; set; }
public List<TracksToArtists> Artists { get; set; }
}
public class Album
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Track> Tracks { get; set; }
// TODO: create album-to-artist many-to-many relationship
// public List<SomethingArtistMaybe?> Artists { get; set; }
}
public class Artist
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<TracksToArtists> Tracks { get; set; }
// TODO: create album-to-artist many-to-many relationship
// public List<SomethingAlbumMaybe?> Albums { get; set; }
}
// many-to-many relationship
public class TracksToArtists
{
public Guid TrackId { get; set; }
public Track Track { get; set; }
public Guid ArtistId { get; set; }
public Artist Artist { get; set; }
}
如果只使用原始 SQL 查询,这是非常简单的,但是使用 ORM 一切都会变得有点痛苦。
Optimly,我想避免引入 AlbumToArtist
table,因为它可能会造成数据不一致。它应该是这样的:
如果我没理解错的话,Artist
与 Album
没有直接关系,关系仅通过 Track
.
考虑把Album
放在中间,这样每个Artist
有多个Album
,反之亦然(多对多),然后有Album
导航到许多 Track
(一对多)。
无论如何,无论您是选择按照您的问题还是按照我的建议保留您的模型,从一个点到另一个点访问图形(在您的问题 Album
中每个 Artist
),使用eager loading:
public async Task<IEnumerable<Album>> GetAlbums(Artist artist)
{
return await myDbContext.Artists
.Include(artist => artist.Tracks)
.ThenInclude(track => track.Album)
.Where(a.Id == artist.Id)
.Select(artist =>
artist.Tracks.Select(track =>
track.Album))
.Distinct()
.ToListAsync();
}
您还可以启用 lazy-loading,这样您就不必在查询中明确包含导航属性,但这样做会产生性能成本。
考虑以下 3 个实体 A
、B
和 C
。 A
-to-C
是多对多关系。 B
-to-A
是一对多的关系(B
有很多A
,这意味着B
-to-C
也是多对多)。
如果您觉得这些过于复杂的关系过于模糊,请考虑以下示例:
- 一个
Track
(A
)有多个Artist
(C
),一个Artist
有多个Track
(很多-对多) - 一个
Album
(B
) 有多个Track
。 (一对多,一个Track
不能属于多个Album
)
所以(暗示):
- 一个
Album
有多个Artist
,一个Artist
有多个Album
(多对多)
问题:如何建立这种关系的实体?使用以下代码,我已经建立了Album
-to-Artist
之间的关系,Track
-到-Artist
。 如何创建 Artist
-Album
关系 因为它似乎需要加入 4 tables/entities.
public class Context: DbContext
{
// irrelevant code omitted...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TracksToArtists>()
.HasKey(r => new {r.TrackId, r.ArtistId});
modelBuilder.Entity<TracksToArtists>()
.HasOne(r => r.Track)
.WithMany(t => t.Artists)
.HasForeignKey(r => r.TrackId);
modelBuilder.Entity<TracksToArtists>()
.HasOne(r => r.Artist)
.WithMany(a => a.Tracks)
.HasForeignKey(r => r.ArtistId);
}
}
public class Track
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid AlbumId { get; set; }
public Album Album { get; set; }
public List<TracksToArtists> Artists { get; set; }
}
public class Album
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Track> Tracks { get; set; }
// TODO: create album-to-artist many-to-many relationship
// public List<SomethingArtistMaybe?> Artists { get; set; }
}
public class Artist
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<TracksToArtists> Tracks { get; set; }
// TODO: create album-to-artist many-to-many relationship
// public List<SomethingAlbumMaybe?> Albums { get; set; }
}
// many-to-many relationship
public class TracksToArtists
{
public Guid TrackId { get; set; }
public Track Track { get; set; }
public Guid ArtistId { get; set; }
public Artist Artist { get; set; }
}
如果只使用原始 SQL 查询,这是非常简单的,但是使用 ORM 一切都会变得有点痛苦。
Optimly,我想避免引入 AlbumToArtist
table,因为它可能会造成数据不一致。它应该是这样的:
如果我没理解错的话,Artist
与 Album
没有直接关系,关系仅通过 Track
.
考虑把Album
放在中间,这样每个Artist
有多个Album
,反之亦然(多对多),然后有Album
导航到许多 Track
(一对多)。
无论如何,无论您是选择按照您的问题还是按照我的建议保留您的模型,从一个点到另一个点访问图形(在您的问题 Album
中每个 Artist
),使用eager loading:
public async Task<IEnumerable<Album>> GetAlbums(Artist artist)
{
return await myDbContext.Artists
.Include(artist => artist.Tracks)
.ThenInclude(track => track.Album)
.Where(a.Id == artist.Id)
.Select(artist =>
artist.Tracks.Select(track =>
track.Album))
.Distinct()
.ToListAsync();
}
您还可以启用 lazy-loading,这样您就不必在查询中明确包含导航属性,但这样做会产生性能成本。