MVC implementation of many to many relationships - asp.net-mvc

This is done using MVC .net framework and entity framework "database first" approach. There is a many to many relationship between two tables. They are connected through third table that has combined key as id from first table and id from second table.
public class ManyToManyTable
{
[Required]
[Key, Column(Order=0)]
public int firsttableid { get; set; }
[Required]
[Key, Column(Order=1)]
public int secondtableid { get; set; }
public int something { get; set; }
[ForeignKey("firsttableid")]
public virtual FirstTable firstTable { get; set; }
[ForeignKey("secondtableid")]
public virtual SecondTable secondTable { get; set; }
}
First and Second table have some id which is primary key.
I want to create View and Controller method that enables master detail entry form for this ManyToManyTable. that would have FirstTable in Master and SecondTAble in details, and all to be saved in ManyToManyTable when button Save is pressed.
Of course, both First and Second Table have this property:
public virtual ICollection<ManyToManyTable> ManyToManyTables { get; set; }
What is the easiest way to implement cases like this one?
Thank you!

EF has a default conventions for many-to-many relationships. No need to create specific
mapping class. You have to include navigation properties in both "FirstTable" and "SecondTable" Class as shown below.
public class FirstTable
{
public FirstTable()
{
secondTableProperties = new HashSet<SecondTable>();
}
public int Id { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
public virtual ICollection<SecondTable> secondTableProperties { get; set; }
}
public class SecondTable
{
public SecondTable()
{
FirstTableProperties = new HashSet<FirstTable>();
}
public int Id { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
public virtual ICollection<FirstTable> FirstTableProperties { get; set; }
}
Remove mapping class from DBContext , only include above two classes. Build and run the application , EF will automatically create a Mapping table in SQL server. Usually the Mapping table contains only the primary keys of other two tables.
You can use Fluent API to take some control on the created mapping table
modelBuilder.Entity<FirstTable>()
.HasMany<SecondTable>(s => s.FirstTableProperties)
.WithMany(c => c.secondTableProperties)
.Map(cs =>
{
cs.MapLeftKey("FirstTableId");
cs.MapRightKey("SecondTableId");
cs.ToTable("ManyToManyTable");
});
If you want to work with a join table with additional properties, above mentioned many-to-many relationship won't work . In that case you will have to create two one-to-many relationships as shown below.
public class FirstTable
{
public int Id { get; set; }
public int MyProperty2 { get; set; }
public virtual ICollection<ManyToManyTable> manytomany { get; set; }
}
public class SecondTable
{
public int Id { get; set; }
public int MyProperty2 { get; set; }
public virtual ICollection<ManyToManyTable> manytomany { get; set; }
}
public ManyToManyTable
{
[Required]
[Key, Column(Order=0)]
public int firsttableid { get; set; }
[Required]
[Key, Column(Order=1)]
public int secondtableid { get; set; }
public int AdditionalProperty { get; set; }
public virtual FirstTable first { get; set; }
public virtual SecondTable Second { get; set; }
}

Related

How to include a child object's child object in Entity Framework 5 Not working

I saw this How to include a child object's child object in Entity Framework 5
However, I'm using MVC 5 and that doesn't seem to be working.
I have a typical Tournament, Games, Teams structure, and am trying to include Teams when I query the tournament.
I am trying to run
db.Tournaments.Include(t => t.Games.Select(g => g.Team1)).ToList();
However Team1 is coming back null. Here are my classes:
public class Tournament
{
public int TournamentId { get; set; }
public string Name { get; set; }
public virtual List<Game> Games { get; set; }
public virtual List<Standing> Standings { get; set; }
}
public class Game
{
public int GameId { get; set; }
public Location Location { get; set; }
public int LocationId { get; set; }
public virtual Team Team1 { get; set; }
public int Team1Id { get; set; }
public virtual Team Team2 { get; set; }
public int Team2Id { get; set; }
public int Team1Score { get; set; }
public int Team2Score { get; set; }
public DateTime Time { get; set; }
public Tournament Tournament { get; set; }
public int TournamentId { get; set; }
}
public class Team
{
public int TeamId { get; set; }
public string Name { get; set; }
public string Coach { get; set; }
}
Does anyone have any suggestions?
You are trying to load a single cell from the Games table in the Db, I don't think you can do this the way you are trying too.
You have your Navigation properties set to Virtual, so you should lazy load automatically when you call the property that you need. There is no need for an include statement.
All you normally need to do is iterate through the list of games in tournaments
var Tournaments = Db.Tournaments.ToList();
foreach (var game in Tournaments.Games)
{
game.Team1.TeamId;
game.Team1.Coach;
game.Team1.Name;
}
db.Tournaments.Include(t => t.Games.Team1).ToList();
This is the correct way of Including single child entities in EF5.

code first migration produces empty up down methods when add new model to existing database

I created models and performed code first migration which resulted in prepopulated up down methods.
However at a later stage I added new models to my application. The new models I added were Cart, OrderDetails and Order. I then typed add-migration for each of these models which as a result produced empty up down methods.
I would like to ask why are these up down methods empty when I added a new model.
I referenced these models in the dbcontext, the same dbcontext that referenced previously created models.
These are the new model classes that I added:
public class OrderDetail
{
public int OrderDetailId { get; set; }
public int OrderId { get; set; }
public int BookId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public virtual Book Books { get; set; }
public virtual Order Order { get; set; }
}
public class Cart
{
[Key]
public int RecordId { get; set; }
public string CartId { get; set; }
public int BookId{ get; set; }
public int Count { get; set; }
public virtual Book Book { get; set; }
}
class Order
{
public int OrderId { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
}
Here is my dbcontext
public BookStoreOnlineDB() : base("name=BookStoreOnlineDB")
{
}
public System.Data.Entity.DbSet<BookStoreOnline.Models.Book> Books { get; set; }
public System.Data.Entity.DbSet<BookStoreOnline.Models.Author> Authors { get; set; }
public System.Data.Entity.DbSet<BookStoreOnline.Models.BookStatus> BookStatus { get; set; }
public System.Data.Entity.DbSet<BookStoreOnline.Models.Genre> Genres { get; set; }
public System.Data.Entity.DbSet<BookStoreOnline.Models.Order> Orders { get; set; }
public System.Data.Entity.DbSet<BookStoreOnline.Models.OrderDetail> OrderDetails { get; set; }
public System.Data.Entity.DbSet<BookStoreOnline.Models.Cart>Carts { get; set; }
}
In summary, how do I populate the new up down methods with regards to the newly added Cart, OrderDetail and Detail models.
N.B. The orderDetails model and cart model reference the book model(book model contains had data migration performed on it at an earlier stage and contains populated up down methods).
New to this and would really appreciate help.
Thanks
Answer:
in PM Console:
add-migration initialcreate
//this included the newly added models e.g their ids,(cart,orderdetails, order models) to the up down methods

Entity framework migration of Collections

I have a model that looks like this
public abstract class Item
{
public int ItemId { get; set; }
public String Title { get; set; }
public String Description { get; set; }
public DateTime PurchaseDate { get; set; }
public ICollection<String> Pictures { get; set; }
public Int32 MinimumPrice { get; set; }
public DateTime Deadline { get; set; }
public Boolean VisibleBids { get; set; }
public Boolean Handled { get; set; }
public DateTime PlacementDate { get; set; }
public ApplicationUser User { get; set; }
}
In my controller I do
db.Items.ToList()
This leaves the Pictures field for all fetched objects null because its how the entity framework works. What is a good solution to fetch them in one query?
I hope you already done with Navigation properties between your tables, Now you just need to make your collection virtual and use the concept of eager loading when you need data from both the tables
public virtual ICollection<String> Pictures { get; set; }
and use include in linq like
db.Items.Include("Pictures").ToList()
So here by making virtual navigation you are saying that only take the data of related entity when you needed and whenever you need the data use the Include for eager loading.
For setting navigation properties please have a look on the code.
Suppose the scenario where we have a Post and on this we have multiple comments like
class Posts
{
public int PostsId { get; set; }
public string PostsDescription { get; set; }
public virtual ICollection<Comments> Comments { get; set; }
}
class Comments
{
public int CommentsId { get; set; }
public string CommentsDescription { get; set; }
public int PostsId { get; set; }
public virtual Posts Posts { get; set; }
}

Asp.net Mvc Code First Many to Many with Additional Properties

As far as i know, i have two way to implement many-to-many relation in asp.net mvc using code-first.
1- Fluent Api
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrPersonTitle> HrPersonTitle { get; set; }
}
public class HrPersonTitle
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<HrPerson> HrPerson { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<HrPerson>()
.HasMany(s => s.HrPersonTitle)
.WithMany(c => c.HrPerson)
.Map(t =>
{
t.MapLeftKey("HrPersonId")
.MapRightKey("HrPersonTitleId")
.ToTable("HrMapPersonTitle");
});
}
2-Custom Mapping Table
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapPersonTitle> HrMapPersonTitle { get; set; }
}
public class HrPersonTitle
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<HrMapPersonTitle> HrMapPersonTitle { get; set; }
}
public class HrMapPersonTitle
{
public int Id { get; set; }
public int HrPersonId { get; set; }
public int HrPersonTitleId { get; set; }
public virtual HrPerson HrPerson { get; set; }
public virtual HrPersonTitle HrPersonTitle { get; set; }
public string Note { get; set; }
public bool Deleted { get; set; }
}
My questions:
If i choose second way, i am not able to reach HrPersonTitle.Name property from HrPerson model in the view. How can i reach the properties ?
If i choose the first way i can reach the HrPersonTitle.Name but i am not able to add more property in the map file ? How can i add more properties?
Regards.
When you create a M2M without a payload (just the foreign key relationships, no extra data), EF collapses the relationship so that you can query directly without having to explicitly go through the join table. However, if you need a payload, then EF can no longer manage the relationship in this way.
So, if you want to get the title, you have to go through HrMapPersonTitle:
#foreach (var title in Model.HrMapPersonTitle)
{
#title.HrPersonTitle.Name
}
Both these methods seem overkill maybe. I don't know your full intentions however I implement this all the time at work and I use the following:
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrPersonTitle> HrPersonTitles { get; set; }
}
public class HrPersonTitle
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<HrPerson> HrPersons { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<HrPerson>()
.HasMany(s => s.HrPersonTitles)
.WithMany(c => c.HrPersons);
}
If you are using code first and you try and access either mapping within the DbContext it should Lazy Load your information and every property should be accessible.
I do have one question though. Are you sure it should be many to many, do they really have multiple titles?

MVC3 model set up - Code First EF

I'm trying to create a list of train journeys (among other things) in MVC, using code first Entity Framework and wondered how I could map foreign keys for the stations. The Journey model/table will have a DepartureStationID and an ArrivalStationID which will be foreign keys linking to one table/model, called Station.
Here is the code for both these models:
public class Station
{
public int StationID { get; set; }
public string StationName { get; set; }
public string StationLocation { get; set; }
}
public class Journey
{
public int JourneyID { get; set; }
public int DepartureID { get; set; }
public int ArrivalID { get; set; }
public int OperatorID { get; set; }
public string JourneyCode { get; set; }
public virtual Operator Operator { get; set; }
public virtual Station DepartureStation { get; set; }
public virtual Station ArrivalStation { get; set; }
}
There is another foreign key value in there, namely Operator and that has mapped successfully, but the departure and arrivals haven't, and return null values in the view: (#Html.DisplayFor(modelItem => item.DepartureStation.StationName).
When I looked in the database, there had been two additional fields created by EF:
DepartureStation_StationID
ArrivalStation_StationID
And the SQL relationship was between the station table and the two fields above, rather than DepartureID and ArrivalID
So, my question is - Do I need to do something different in the model when referencing the same table for two fields? I don't know why those additional fields were added so I presume I've set up the model incorrectly.
Thanks
For completeness, here's the same thing with fluent configuration.
public class MyDb : DbContext
{
public DbSet<Journey> Journeys { get; set; }
public DbSet<Operator> Operators { get; set; }
public DbSet<Station> Stations { get; set; }
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<Journey>()
.HasRequired(j => j.DepartureStation)
.WithMany()
.HasForeignKey(j => j.DepartureID);
builder.Entity<Journey>()
.HasRequired(j => j.ArrivalStation)
.WithMany()
.HasForeignKey(j => j.ArrivalId);
// ... Same thing for operator ...
base.OnModelCreating(builder);
}
}
Edit: To address your above comment about the cascade delete, you can add .WillCascadeOnDelete(false) after .HasForeignKey() and that might help (although you'll then have to delete Journey records manually)
Add the folowing attributes on your navigation properties :
public class Journey
{
public int JourneyID { get; set; }
public int DepartureID { get; set; }
public int ArrivalID { get; set; }
public int OperatorID { get; set; }
public string JourneyCode { get; set; }
[ForeignKey("OperatorID")]
public virtual Operator Operator { get; set; }
[ForeignKey("DepartureID")]
public virtual Station DepartureStation { get; set; }
[ForeignKey("ArrivalID")]
public virtual Station ArrivalStation { get; set; }
}
And of course you need to regenerate your database in order to apply the new configuration.
Hope this will help.

Resources