How handle these two models in code first approach using Entity Framework 7?

asp.net-core c# entity-framework entity-framework-core jointable

Question

Problem

I am coding proof of concept ASP.NET 5 MVC6 that will simulate a library. I am using CTP6 and beta3.

How to define their relationship in Entity Framework 7 using code first approach?

Shelf and Books need a join table. As I see it, this application will consider one shelf at a time and the books on that self. In other words, one shelf has a one to many relationship with books.

Models

Shelf model:

public class Shelf
{
    public int ShelfId { get; set; }

    public string ShelfName { get; set; }

    public ShelfType MyProperty { get; set; }

    public virtual List<Book> Books { get; set; }

    public DateTime Created { get; set; }

    public string CreatedBy { get; set; }

    public DateTime Updated { get; set; }

    public string UpdatedBy { get; set; }

}

Book model:

public class Book
{
    public int BookId { get; set; }

    public string BookName { get; set; }

    public string BookAuthor { get; set; }

    public string BookGenre { get; set; }

    public DateTime BookPublishedDate { get; set; }

    public string Description { get; set; }

    public DateTime Created { get; set; }

    public string CreatedBy { get; set; }

    public DateTime Updated { get; set; }

    public string UpdatedBy { get; set; }

}

Goal

I would like to implement something like this in ASP.NET 5. Since TPH and other features are not yet implemented in EF7, am I on the right track?

In other words, I am thinking the following:

  • ShelfBookViewModel (after all I need a view model to show a shelf and the books, or the book and be able to nagivate back to the shelf). I probably will just name it ShelfBook. This seems to be the wrong approach.
  • I would prefer EF7 being able to figure out I have a join and creating viewmodels for separate concerns. Otherwise, this is a violation of SOLID code practices. Something like given below (from the link earlier).
  • Join tables have not been implemented in EF7 yet and I am asking in vain?

I have not been able to get the following to work as shown below:

 modelBuilder.Entity<Shelf>() 
            .HasMany(p => p.Shelfs) 
            .WithMany(t => t.Books) 
            .Map(mc => 
               { 
                   mc.ToTable("ShelfJoinBook"); 
                   mc.MapLeftKey("ShelfId"); 
                   mc.MapRightKey("BookId"); 
               });

Thank you for your time in reading my question. I hope others find this useful.

Popular Answer

To answer your question about many-to-many, you should define your mapping as follows (and, necessarily, update your model classes):

modelBuilder.Entity<Shelf>() 
    .HasMany(p => p.Books) 
    .WithMany(t => t.Shelves) // doesn't exist 
    .Map(mc => 
       { 
           mc.ToTable("BookShelves"); 
           mc.MapLeftKey("ShelfId"); 
           mc.MapRightKey("BookId"); 
       });

which would require you to add an ICollection<Shelf> Shelves { get; set; } to Book. But, if you did so you'd then have the following (this is my MCVE version of your example):

public class Shelf
{
    public int ShelfId { get; set; }
    public virtual ICollection<Book> Books { get; set; }
}

public class Book
{
    public int BookId { get; set; }
    public virtual ICollection<Shelf> Shelves { get; set; }
}

and then you'd no longer need the mapping anyway, since EF would generate all the relations for you by convention, including the join table that would be visible only in the database itself.

However – from your actual model classes and your discussion around them – it appears you really only need one-to-many, in which case, you should accept Pynt's answer, and ignore mine.

EDIT

As pointed out by Gert Arnold in comments, EF7 does not currently (no longer?) supports generated join tables, which means we'll have to roll our own, which is probably better anyway.

Here's a way I think I might do my own, simple, join tables from now on, if EF will no longer do them for me (warning — I'm just trying to close the loop on this issue ... I'm not bothering to try this in a compiler, so YMMV):

public class Shelf
{
    public int ShelfId { get; set; }
    public virtual ICollection<BookShelf> BookShelves { get; set; }
    public virtual IQueryable<Book> Books
    {
        get
        {
            return BookShelves.Where(s => s.ShelfId == ShelfId)
                              .Select(s => s.Book);
        }
    }
}

public class Book
{
    public int BookId { get; set; }
    public virtual ICollection<BookShelf> BookShelves { get; set; }
    public virtual IQueryable<Shelf> Shelves
    {
        get
        {
            return BookShelves.Where(s => s.BookId == BookId)
                              .Select(s => s.Shelf);
        }
    }
}

public class BookShelf
{
    [Key]
    public int BookId { get; set; }
    [ForeignKey("BookId")]
    public Book Book { get; set; }
    [Key]
    public int ShelfId { get; set; }
    [ForeignKey("ShelfId")]
    public Shelf Shelf { get; set; }
}


Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why