实体框架 - 外键约束

时间:2014-11-24 22:07:22

标签: c# asp.net-mvc database entity-framework-6

我在数据库中有3个表:

歌曲(ID,Title,ReleaseDate)
相册(ID,标题,发布日期)
艺术家(ID,FirstName,LastName)

我有一个相关表格,以便歌曲可以与专辑或艺术家相关,或两者兼而有之:

RelatedSong (ID,ParentID,SongID,TrackNumber)(Album.ID上的外键和ParentID的Artist.ID以及SongID的Song.ID)

因此,使用这四个表,我希望实体框架能够生成允许我在我的MVC项目中简单地执行和运行的模型,但由于外键约束而导致保存失败。如果我设置ParentID = Album.ID,那么它会抱怨Artist.ID为NULL,反之亦然。有什么建议?我正在为现有应用程序重写前端,因此数据库无法更改。我需要知道如何构建模型以便这样做。它位于模型或modelBuilder(Fluent API)中。

专辑模型:

[Table("Album")]
public partial class Album
{
    public Album()
    {
        RelatedAlbums = new HashSet<RelatedAlbum>();
        RelatedSongs = new HashSet<RelatedSong>();
    }

    public Guid ID { get; set; }

    [Required]
    public string Title { get; set; }

    public DateTime ReleaseDate { get; set; }

    public virtual ICollection<RelatedAlbum> RelatedAlbums { get; set; }

    public virtual ICollection<RelatedSong> RelatedSongs { get; set; }
}

艺术家模特:

[Table("Artist")]
public partial class Artist
{
    public Artist()
    {
        RelatedAlbums = new HashSet<RelatedAlbum>();
        RelatedSongs = new HashSet<RelatedSong>();
    }

    public Guid ID { get; set; }

    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    public virtual ICollection<RelatedAlbum> RelatedAlbums { get; set; }

    public virtual ICollection<RelatedSong> RelatedSongs { get; set; }
}

相关相册:

[Table("RelatedAlbum")]
public partial class RelatedAlbum
{
    public Guid ID { get; set; }

    public Guid ParentID { get; set; }

    public Guid AlbumID { get; set; }

    public virtual Album Album { get; set; }

    public virtual Artist Artist { get; set; }
}

相关歌曲:

[Table("RelatedSong")]
public partial class RelatedSong
{
    public Guid ID { get; set; }

    public Guid ParentID { get; set; }

    public Guid SongID { get; set; }

    public int? TrackNumber { get; set; }

    public virtual Album Album { get; set; }

    public virtual Artist Artist { get; set; }

    public virtual Song Song { get; set; }
}  

曲:

[Table("Song")]
public partial class Song
{
    public Song()
    {
        RelatedSongs = new HashSet<RelatedSong>();
    }

    public Guid ID { get; set; }

    [Required]
    public string Title { get; set; }

    public DateTime ReleaseDate { get; set; }

    public virtual ICollection<RelatedSong> RelatedSongs { get; set; }
}

的DbContext:

public partial class MusicDbContext : DbContext
{
    public MusicDbContext()
        : base("name=MusicDbContext")
    {
    }

    public virtual DbSet<Album> Albums { get; set; }
    public virtual DbSet<Artist> Artists { get; set; }
    public virtual DbSet<RelatedAlbum> RelatedAlbums { get; set; }
    public virtual DbSet<RelatedSong> RelatedSongs { get; set; }
    public virtual DbSet<Song> Songs { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Album>()
            .HasMany(e => e.RelatedAlbums)
            .WithRequired(e => e.Album)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Album>()
            .HasMany(e => e.RelatedSongs)
            .WithRequired(e => e.Album)
            .HasForeignKey(e => e.ParentID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Artist>()
            .HasMany(e => e.RelatedAlbums)
            .WithRequired(e => e.Artist)
            .HasForeignKey(e => e.ParentID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Artist>()
            .HasMany(e => e.RelatedSongs)
            .WithRequired(e => e.Artist)
            .HasForeignKey(e => e.ParentID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Song>()
            .HasMany(e => e.RelatedSongs)
            .WithRequired(e => e.Song)
            .WillCascadeOnDelete(false);
    }
}

更新
下面是Create方法的控制器代码。

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,ParentID,SongID,TrackNumber")] RelatedSong relatedSong)
{
    if (ModelState.IsValid)
    {
        relatedSong.ID = Guid.NewGuid();
        db.RelatedSongs.Add(relatedSong);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    ViewBag.ParentID = new SelectList(db.Albums, "ID", "Title", relatedSong.ParentID);
    ViewBag.SongID = new SelectList(db.Songs, "ID", "Title", relatedSong.SongID);

    return View(relatedSong);
}

更新2:
也许数据库模型不正确或什么?不知道为什么这是不可能的,因为在我看来,这是将数据与多个父母&#34;相关联的最有效方式。我刚刚阅读了另一篇文章,说它不可能(但为什么数据库设计师会允许我这样做?)...

请参阅:Multiple foreign keys to a single column

4 个答案:

答案 0 :(得分:1)

你的问题在这里:

1

    modelBuilder.Entity<Album>()
        .HasMany(e => e.RelatedAlbums)
        .WithRequired(e => e.Album)
        .WillCascadeOnDelete(false);

应该有WithOptional(e => e.Album)

2

    modelBuilder.Entity<Album>()
        .HasMany(e => e.RelatedSongs)
        .WithRequired(e => e.Album)
        .HasForeignKey(e => e.ParentID)
        .WillCascadeOnDelete(false);

应该有WithOptional(e => e.Album)

3

    modelBuilder.Entity<Artist>()
        .HasMany(e => e.RelatedAlbums)
        .WithRequired(e => e.Artist)
        .HasForeignKey(e => e.ParentID)
        .WillCascadeOnDelete(false);

应该有WithOptional(e => e.Artist)

4

    modelBuilder.Entity<Artist>()
        .HasMany(e => e.RelatedSongs)
        .WithRequired(e => e.Artist)
        .HasForeignKey(e => e.ParentID)
        .WillCascadeOnDelete(false);

应该有WithOptional(e => e.Artist)

5

    modelBuilder.Entity<Song>()
        .HasMany(e => e.RelatedSongs)
        .WithRequired(e => e.Song)
        .WillCascadeOnDelete(false);

应该有WithOptional(e => e.Song)

您写道它们不是必需的,但在配置中您需要它们。您应该将外键属性设置为可空类型。

[表(&#34; RelatedSong&#34)] public partial class RelatedSong {     公共Guid ID {get;组; }

public Guid? ParentID { get; set; }

...

}

[表(&#34; RelatedAlbum&#34)] public partial class RelatedAlbum {     公共Guid ID {get;组; }

public Guid? ParentID { get; set; }

public Guid? AlbumID { get; set; }
...

}

等等。

答案 1 :(得分:0)

您正在尝试插入“相关歌曲”而没有“相册”,这是

中必需的
if (ModelState.IsValid)
{
    relatedSong.ID = Guid.NewGuid();
    db.RelatedSongs.Add(relatedSong);
    db.SaveChanges();
    return RedirectToAction("Index");
}

如果你的关系/类型设置略有不同,你可以使用这样的东西

 if (ModelState.IsValid)
    {
       Song song = GetSongById(originalSongId, db);
        Song relatedSong = GetSongById(relatedSongId, db);
        song.RelatedSongs.Add(relatedSong);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

答案 2 :(得分:0)

你可以在Song表中有两个FK,并使两者都可以为空,在这种情况下,你可以在没有第三张表的情况下将一首歌引用到两者,同时EF可以完美地工作。

歌曲(ID,Title,ReleaseDate, AlbumID可空,ArtistID可以为空

专辑(ID,Title,ReleaseDate)

艺术家(ID,FirstName,LastName)

答案 3 :(得分:0)

此处更正了您的代码

Song(ID,Title,ReleaseDate)
Album(ID,Title,ReleaseDate)
Artist(ID,FirstName,LastName)
RelatedSong(ID,ParentID,SongID,ArtistID,AlbumID,TrackNumber)

[Table("RelatedSong")]
public partial class RelatedSong
{
    public Guid ID { get; set; }    
    public Guid ParentID { get; set; }    // this will be used for the Parent Song
    public Guid SongID { get; set; }    
    public Guid ArtistId {get; set;} // this will be used for artist foreign key
    public Guid AlbumId {get; set;} // this will be used for album foreign key
    public int? TrackNumber { get; set; }    
    public virtual Album Album { get; set; }    
    public virtual Artist Artist { get; set; }    
    public virtual Song Song { get; set; }
}  

 modelBuilder.Entity<Album>()
            .HasMany(e => e.RelatedSongs)
            .WithRequired(e => e.Album)
            .HasForeignKey(e => e.ParentID) // here you should use AlbumId and not ParentID
            .WillCascadeOnDelete(false);

modelBuilder.Entity<Artist>()
            .HasMany(e => e.RelatedSongs)
            .WithRequired(e => e.Artist)
            .HasForeignKey(e => e.ParentID) // here you should use ArtistId and not ParentID, which you already used it in the Album above

 modelBuilder.Entity<Song>()
            .HasMany(e => e.RelatedSongs)
            .WithRequired(e => e.Song)
            .HasForeignKey(e=>e.ParentID); // here you will use the parent id for the song relation
            .WillCascadeOnDelete(false);

基于此,你可以解决其他问题,如果有的话

希望这会对你有所帮助