ASP.NET MVC Many to Many relationship, using "My Own" table - asp.net-mvc

I am fairly new to using Code First approach with entity framework and I know that I you have a many to many relationship like the entities below, the EF will create the intermediary table automatically:
class Post {
...
public virtual ICollection<Category> Categories {get; set;}
...
}
class Category {
...
public virtual ICollection<Post> Posts {get; set;}
...
}
However, if in the intermediary table I need to have extra data fields, one possible way (which I currently like, maybe because I am unaware of better ways) would be defining a new Entity of my own, like:
class Posts_Categories {
public int Id {get; set;}
public int CategoryId {get; set;}
public int PostId {get; set;}
public string Exrtafield1 {get; set;}
public int ex extraField2 {get; set;}
...
public virtual Post Post {get; set;}
public virtual Category Category {get; set;}
}
Using this approach, EF does create my custom intermediary table, but it also creates another one of its own called "PostsCategories" which only contains a foreign key to Post_Id and another to Category_Id.
How do I make it not create that extra one and use the one I have defined?
Is this a good way to manage Many to Many relationships with extra data fields??

you should use one to many relation like this :
public class Post
{
public System.Int32 PostId { get; set; }
[InverseProperty("Post")]
public virtual ICollection<Posts_Category> PostCategories { get; set; }
}
public class Category
{
public System.Int32 CategoryId { get; set; }
[InverseProperty("Category")]
public virtual ICollection<Posts_Category> PostCategories { get; set; }
}
public class Posts_Category
{
public System.Int32 PostId { get; set; }
public System.Int32 CategoryId { get; set; }
[ForeignKey("PostId")]
[InverseProperty("PostCategories")]
public virtual Post Post { get; set; }
[ForeignKey("CategoryId")]
[InverseProperty("PostCategories")]
public virtual Category Category { get; set; }
}

I needed to expand a bit on Iraj's answer to make it work. Another modification is that I'm including the default ApplicationUser as one of my tables.
So the relation is ApplicationUser 1-∞ IdeaVote ∞-1 Idea (i.e. there are users and ideas, users can vote on ideas, and each vote is represented with a connection between an ApplicationUser and an Idea.
public class Idea
{
[Key]
public int Id { get; set; }
// This is an ordinary data field
public string Text { get; set; }
[InverseProperty("Idea")]
public virtual ICollection<IdeaVote> Votes { get; set; }
}
public class IdeaVote
{
[Key]
public int Id { get; set; }
public int IdeaId { get; set; }
[ForeignKey("IdeaId")]
[InverseProperty("Votes")]
public virtual Idea Idea { get; set; }
public string UserId { get; set; }
[ForeignKey("UserId")]
[InverseProperty("Votes")]
public virtual ApplicationUser User { get; set; }
}
public class ApplicationUser : IdentityUser
{
[InverseProperty("User")]
public virtual ICollection<IdeaVote> Votes { get; set; }
// Default stuff
}

It is normal for it to create that PostsCategories table for the relation between the two, and you are going to want that. If c is a Category, you'll be able to do things like c.Posts
Normally you would not create your own table manually for that. What kinds of data would you be keeping in the "extra" fields? I would probably move the fields to one of the other tables and drop that one. Most many to many relationship tables do not contain extra fields.

Related

How can I query Entity Framework to a different class and get a count instead of a list property?

Let's take an example model:
public class Person
{
public string Name {get; set;}
public List<Guitar> Guitars {get; set;}
}
public class Guitar
{
public string Brand {get; set;}
public string Model {get; set;}
}
I need to create a View with a list of all People and a count of how many guitars they have. To do that I would like to pass a ViewModel like this one populated with EF without loading all Guitards:
// This will be send to the View, I can also just send a List
public class ViewModelPassed
{
List<PeopleGuitarViewModel> AllPeople { get; set; }
}
// This should be populated from EF
public class PeopleGuitarViewModel
{
public string Name { get; set; }
public int NumberOfGuitars { get; set; }
}
Can I query EF to a different class, and avoid bringing the whole list of guitars and instead get a COUNT in SQL?
Thanks!
You should provide primary key to your models for EF core to create the relationship:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public List<Guitar> Guitars { get; set; }
}
public class Guitar
{
public int ID { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
}
Also use public for AllPeople property in ViewModelPassed:
public List<PeopleGuitarViewModel> AllPeople { get; set; }
Then query like :
ViewModelPassed modelPassed = new ViewModelPassed();
modelPassed.AllPeople = new List<PeopleGuitarViewModel>();
modelPassed.AllPeople = _applicationDbContext.Persons.Select(x =>
new PeopleGuitarViewModel
{
Name = x.Name,
NumberOfGuitars = x.Guitars.Count
}).ToList();
One way is to do it like this:
var viewModel=context.Persons.Select(x=>new PeopleGuitarViewModel
{
Name=x.Name,
NumberOfGuitars=Guitars.Count()
}).ToList();
this kind of query is also named Projection and is consider best practice (do not send full entity to the view).
There are 2 libraries which I often use for such projections: AutoMapper and/or Mapster.
Hope this help you.
When populating your ViewModel, you can use Linq to get the count. Like this:
PeopleGuitarViewModel p= new PeopleGuitarViewModel();
p.Name = person.Name;
p.NumberOfGuitars = person.Guitars.Count;
Where person is an object of your Person class.

Entity Framework several One-to-Many with same model

My Order class
public class Order
{
[Key]
public int ID { get; set; }
(...)
[ForeignKey("Client")]
public string ClientID { get; set; }
public virtual ApplicationUser Client { get; set; }
[ForeignKey("Trader")]
public string TraderID { get; set; }
public virtual ApplicationUser Trader { get; set; }
[ForeignKey("Driver")]
public string DriverID { get; set; }
public virtual ApplicationUser Driver { get; set; }
}
And my MS Identity ApplicationUser class:
public class ApplicationUser : IdentityUser
{
(...)
public virtual List<Order> Orders { get; set; }
}
As you can see I'd like to have speciffic users in speciffic "role" in Order model. How should I write a code in ApplicationUser to get speciffic lists of Clients, Traders and Drivers? I mean, I'd like to find user in database and then I'd like to have three lists named e.g. AsClient, AsTrader and AsDriver. Right now List<Orders> count is always 0.
Use InversePropertyAttribute:
public class ApplicationUser : IdentityUser
{
//another stuff...
[InverseProperty("Client")]
public virtual ICollection<Order> AsClient { get; set; }
[InverseProperty("Trader")]
public virtual ICollection<Order> AsTrader { get; set; }
[InverseProperty("Driver")]
public virtual ICollection<Order> AsDriver { get; set; }
}
I would suggest you use a little inheritance here.
So you would create three classes that extend ApplicationUser, one for each Trader, Client and Driver. And then on each of these classes have the orders list. Also, on the Order class, change the types from ApplicationUser to the appropriate subtype.
Then as an inheritance strategy you can choose the table per hierarchy and follow the instructions on this link to implement it.
Hope this helps.

Entity Framework defining tables in db context

I am buys designing the model below:
public class LogModel
{
public class UserActivityLogs
{
[Key]
public int id { get; set; }
//Id of the user
public string userId { get; set; }
//Time of the log
public DateTime time { get; set; }
public LogActions action { get; set; }
}
// Types of actions to log
public class LogActions
{
[Key]
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
}
}
Now what I would like to know is do I need to add a table in the context for Logactions as well as UserActivityLogs or will EF see that the two tables are linked and create the log action table automatically?
Also have I specified my relationships correctly? What I was aiming for is that I can define multiple types of Logactions and then a userlog will then have a single log action associated to it.
First, don't use nested classes, it's a needless complication. Use namespaces to organize classes.
Second, don't use plural names for classes. One instance of class represents one entity. Also, use CamelCase names for properties.
Third, yes, Entity Framework will be aware of the associations between the two classes and create a database model with two tables and a foreign key.
So this leaves you with:
namespace MyApp.LogModel
{
public class UserActivityLog
{
[Key]
public int Id { get; set; }
public string UserId { get; set; }
public DateTime Time { get; set; }
public LogAction LogAction { get; set; }
}
public class LogAction
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}

Scaffold MVC - Multiple Models per View

I am trying to use scaffold my models.
My Model looks like this
public class Patient
{
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual int MRN { get; set; }
public virtual ICollection<Issue> Issues { get; set; }
}
public class Issue
{
public virtual int Id { get; set; }
public virtual int PatientId { get; set; }
public virtual Patient Patient { get; set; }
public virtual string Room { get; set; }
public virtual string Comment { get; set; }
}
So I would like it to be when the user adds a patient then the Create Scaffold would show the fields for the Patient and also for the Issue. What I am getting now is just the Patient fields. Is there a way to go about this?
I would like to give the option when creating a new patient, the customer gets the ability to add multiple Issues for the patient.
the default scaffolding stuff in MVC3, and even 4, to my knowledge will not handle this. there are too many different and complex variables for scaffolding to really be reliable in my opinion. you'll need to wire this up yourself. i would suggest creating an editor for your types. that will simplify things at least a little bit.

Navigation property of entity is not loading

I am building a reservation system. I have users in roles('admin', 'client', 'employee', 'student').
Each reservation must be associated with a user of role client, it might be assigned to user of role employee and might also be assigned to user of role student.
So in my reservation class I have properties of type User and I have marked them with [ForeignKey("AnytypeId")] attribute to hint EF for relations.
I have seen code like this at http://blog.stevensanderson.com/2011/01/28/mvcscaffolding-one-to-many-relationships/
public class Reservation
{
public int ReservationID
{
get;
set;
}
[Required(ErrorMessage="Please provide a valid date")]
public DateTime ReservationDate
{
get;
set;
}
public DateTime ReservationEnd { get; set; }
public DateTime EntryDate
{
get;
set;
}
public DateTime UpdatedOn
{
get;
set;
}
public decimal Ammount
{
get;
set;
}
public decimal? Discount { get; set; }
[DataType(DataType.MultilineText)]
public string ServiceDetails { get; set; }
[DataType(DataType.MultilineText)]
public string Remarks { get; set; }
public String PaymentMethod { get; set; }
public string VoucherNumber { get; set; }
public int ServiceID
{
get;
set;
}
public virtual Service Service
{
get;
set;
}
public string EmployeeID { get; set; }
[ForeignKey("EmployeeID")]
public virtual User Employee { get; set; }
public string ClientID { get; set; }
[ForeignKey("ClientID")]
public virtual User Client { get; set; }
public string StudentID { get; set; }
[ForeignKey("StudentID")]
public virtual User Student { get; set; }
}
public class ReservationMap : EntityTypeConfiguration<Reservation>
{
public ReservationMap()
{
this.HasOptional(r => r.Client).WithMany().WillCascadeOnDelete(true);
this.HasOptional(r => r.Employee).WithMany().WillCascadeOnDelete(false);
this.HasOptional(r=>r.Student).WithMany().WillCascadeOnDelete(false);
}
}
Now as I run my mvc3 EF code first app database created for me on the fly with following ERD and edmx model.
Now few problems that I am having:
1. When I am listing all the users of role clients in view their reservation property is showing always 0 even if their are reservations available in database. I don't know why this collection property marked with virtual is not loading??
Please I am stuck with this help me out here this is the last thing remaining.
There are couple of problems in your model. You have configured the one to many relationship in such a way that the many end(Reservations property) is excluded from the mapping. Hence the Reservations will not be loaded by EF.
The other problem is if you are going to map the Reservations property as the many end of the relationship, what will be the navigational property? is it Employee, Client, Student? Because only one of these properties can participate in the relationship with Reservations property.
It is not clear as to how these relationships should be modeled by your description. One way would be to have 3 collection properties.
public class ReservationMap : EntityTypeConfiguration<Reservation>
{
public ReservationMap()
{
HasOptional(r => r.Client).WithMany(u => u.ClientReservations).WillCascadeOnDelete(true);
HasOptional(r => r.Employee).WithMany(u => u.EmployeeReservations).WillCascadeOnDelete(false);
HasOptional(r=>r.Student).WithMany(u => u.StudentReservations).WillCascadeOnDelete(false);
}
}

Resources