4

我正在建模音乐曲目和专辑,其中专辑有许多曲目,曲目只能在一张专辑上,并带有一个连接表来指定它在专辑列表中的位置。

这是我的模型:

public class Track
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int AlbumTrackId { get; set; }
    public virtual AlbumTrack AlbumTrack { get; set; }
}

public class Album
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<AlbumTrack> AlbumTracks { get; set; }
}

public class AlbumTrack
{
    public int AlbumId { get; set; }
    public virtual Album Album { get; set; }

    public int TrackId { get; set; }
    public virtual Track Track { get; set; }

    public int Position { get; set; }
}

和我的 EntityTypeConfiguration

public class AlbumTrackConfiguration : EntityTypeConfiguration<AlbumTrack>
{

    public AlbumTrackConfiguration()
    {
        // AlbumTrack has a composite key
        HasKey(at => new {at.AlbumId, at.TrackId});

        // AlbumTrack has one Album, Albums have many AlbumTracks
        HasRequired(at => at.Album)
            .WithMany(a => a.AlbumTracks)
            .HasForeignKey(at => at.AlbumId)
            .WillCascadeOnDelete(true);

        // AlbumTrack has one Track, Tracks have one AlbumTrack
        HasRequired(at => at.Track)
            .WithRequiredPrincipal(t=>t.AlbumTrack)
            .WillCascadeOnDelete(true);
    }

}

Albums 和 AlbumTracks 之间的一对多关系很好,但除了预期之外

[AlbumTrackId] [int] NOT NULL,

Code First 不断添加

[AlbumTrack_AlbumId] [int] NOT NULL,
[AlbumTrack_TrackId] [int] NOT NULL

到轨道表。

如何更好地对音轨到专辑音轨的关系进行建模,以便只有我指定的属性才能在 db 字段中进行翻译?

[是的,在我的世界里,一首歌只能存在于一张专辑中!]

谢谢。

4

2 回答 2

5

正如建议的那样,将位置作为一部分是Track有意义的。
但是,如果您仍然希望您的“加入”表正常工作-我个人总是宁愿将曲目、专辑与其“关联”(即位置等)分离...

为此,您需要稍微重新组织您的关系。EF/CF 未能构建它 - 因为您要求它本身不支持或预期的东西。加入,索引表总是期待另一边的“多重性”。

基本上,您AlbumTrack不再是一个“加入”表 - 而只是一个one-on-one带有来自 AlbumTrackTrack的 fk 的 + 。Album

考虑到这一点,您可以执行以下操作 - 这将创建正确的列、索引......

public class Track
{
    public int Id { get; set; }
    public string Name { get; set; }
    // public int AlbumTrackId { get; set; }
    public virtual AlbumTrack AlbumTrack { get; set; }
}
public class AlbumTrack
{
    public int TrackId { get; set; } // <== this is a single primary
    public virtual Track Track { get; set; }

    public int AlbumId { get; set; }
    public virtual Album Album { get; set; }

    public int Position { get; set; }
}
public class Album
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<AlbumTrack> AlbumTracks { get; set; }
}

...并使用流畅的配置:

modelBuilder.Entity<AlbumTrack>()
    .HasKey(at => new { at.TrackId });
    //.HasKey(at => new { at.AlbumId, at.TrackId });

modelBuilder.Entity<AlbumTrack>()
            .HasRequired(at => at.Track)
            .WithOptional(a => a.AlbumTrack);
            // .WithRequiredPrincipal(x => x.AlbumTrack);

modelBuilder.Entity<AlbumTrack>()
    .HasRequired(at => at.Album)
    .WithMany(a => a.AlbumTracks)
    .HasForeignKey(at => at.AlbumId)
    .WillCascadeOnDelete(true);

...您可以像这样使用它:

var album1 = db.Albums.Add(new Album { Name = "Track1", });

var tr1 = db.Tracks.Add(new Track { Name = "Track1", });
var tr2 = db.Tracks.Add(new Track { Name = "Track2", });
var tr3 = db.Tracks.Add(new Track { Name = "Track3", });
var tr4 = db.Tracks.Add(new Track { Name = "Track4", });
var tr5 = db.Tracks.Add(new Track { Name = "Track5", });

db.AlbumTracks.Add(new AlbumTrack { Track = tr1, Album = album1, Position = 1 });
db.AlbumTracks.Add(new AlbumTrack { Track = tr2, Album = album1, Position = 2 });
db.AlbumTracks.Add(new AlbumTrack { Track = tr3, Album = album1, Position = 3 });
db.AlbumTracks.Add(new AlbumTrack { Track = tr4, Album = album1, Position = 4 });
db.AlbumTracks.Add(new AlbumTrack { Track = tr5, Album = album1, Position = 5 });

db.SaveChanges();

(或者您可以只“添加”专辑曲目,其余的将进入)。

注意:您不再需要复合键 - 因为您的 AlbumTrack 基本上仅依赖于曲目 - 即only one record每个曲目都有 - 带有“专辑”要附加到。
你也不需要,// public int AlbumTrackId { get; set; }因为那是对面,fk 在专辑曲目中。这在旧设置中也是错误的(因为您需要 fk 的“两个键” - 加入表)。

而且,要补充一点,整个结构基本上“暗示”您只是将 Track 表“拆分”为两个一对一的表,并且您也可以只使用 Track/Album。但这有其自身的优势——虽然你有额外的连接和读/写(不利的一面),但你可以获得一些灵活性——或者例如稍后你可以通过普通的连接表等来关联它们。

于 2013-04-01T15:54:17.927 回答
0

不完全是一个答案,但我不明白为什么您需要一个中间表作为该职位。为什么不这样做:

public class Track
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Position { get; set; }
}

public class Album
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Track> Tracks { get; set; }
}
于 2013-04-01T12:50:28.173 回答