Many to Many in EF core - ef-core-2.0

I have the following models
public class Student
{
public int Id {get; set; }
public string Name {get; set; }
public ICollection<StudentToCourse> StudentToCourse {get; set; }
}
public class StudentToCourse
{
public int StudentId{get; set; }
public Student Student {get; set; }
public int CourseId{get; set; }
public Course Course {get; set; }
}
public class Course
{
public int Id {get; set; }
public string Name {get; set; }
public ICollection<StudentToCourse> StudentToCourse {get; set; }
}
I want to get a list of all COURSE per student ID, how do I go about doing that?

Short answer: SELECT and match on the Id values. Or programatically -
foreach(Student s in yourstudentList)
{
foreach(StudentToCourse stc in s.StudentToCourse)
{
if(stc.StudentId = s.Id)
//this is one you want, do something with it
}
}
Better answer:
First let's look at your model...
Imagine the underlying database structure.
Your middle table is known as a lookup.
You don't really need the entire Student, nor the entire Course object in it.
Also, your Course object does not need to know about either of the other objects.
If you can imagine three tables in your database you can see how you would logically connect a Student to a Course across the three tables.
Your model is still incomplete though. Within your application you still need a container, in this case a List courseIds. In this way your Student doesn't need to care about all the entries in the Student/Course lookup table, just the ones applicable to the particular Student. And you have an easily accessible object to pull data from, or send updates to, the database.
On initial population of the courseIds collection you would do a
SELECT FROM StudentToCourse where StudentId = x
You can then JOIN on your Course table to pull values such as the Course name.
If you find you need to do a lot of those look ups you may cache your Course list and lower your database traffic at the cost of some ram.
Each time you make a new student you would want to populate their list of course Ids and when committing the student to the database you would save your look up table.
This keeps your objects as lightweight as possible while maintaining their relationships.
There are various ways to write the SELECT statement in your dev environment, search them out and find one you like (or that matches your company's current practices) and get used to consistently using the same one. It will keep your code more readable and manageable.

Related

efcore zero to one relationship cascade from principal

in efcore 2.0 I'm trying to figure out how to configure this:
let say I have two table Upload and BlogPost and some more table that save their upload URL into upload. so the upload table doesn't need to have a FK to any other table that wants to save into it
class Upload{
public int Id{get; set;}
public string FileName{get;set;}
public string SavedPath{get; set;}
}
//but each blog post need to know about its own upload
class BlogPost{
public int Id { get; set; }
public int Content { get; set; }
public Upload Upload { get; set; }
}
according to this article, any table that holds the FK is the dependant and any table that hold the primary key is the principal.
but in this case, the BlogPost is the principal and Upload is the dependent. if I delete the BlogPost then the upload must delete, not the otherwise.
modelBuilder.Entity<BlogPost>(b=>{
b.HasOne(bp=>bp.Upload).WithOne().OnDelete(DeleteBehavior.Cascade);
})
according to OnDelete document, it delete the dependant if the the principal deleted, that in my case should be BlogPost, but because the Upload is not carrying any FK, efcore apply cascade wrongly.
my question is how do i make the efcore to get the BlogPost as the principal? or how should i approach this kind of scenario?

How to configure one to zero and one to one relationship in fluent api with different PK and FK

I am a beginner and learning fluent API from tutorials. I read the some of the already given SO solution about 1-0, 1-* but I could not understand them properly. I simply want to use fluent API to set up One to One and One to Zero Relationship provided no convention followed.
House and Room (terrible example)
Requirement:
1. One House can have zero room or ONE room MAX.
2. One Room must be inside a house.
3. If a room is deleted House SHOULD NOT Get Deleted
4. If a House is getting deleted and there is a room then
User should first delete the room then only House should be allowed to be deleted.
public class House
{
public int HouseId { get; set; }
public string HouseName { get; set; }
public virtual Room Room { get; set; }
}
public class Room
{
public int RoomId { get; set; }
public string RoomName { get; set; }
public int HouseId { get; set; }
public virtual House House { get; set; }
}
So, Room cannot exist w/o a House but a House can exist w/o a room. Also, in case if a House has room, it can only have one.
dBModelBuilder.Entity<House>()
.HasOptional(f => f.Room)
.WithRequired(s => s.House);
I saw some solution but they are telling to set the PK and FK same. But I don't want to do that. Is there a way to achieve what I want w/o setting PK and FK same. I DO NOT want the HouseID to be PK of my Room class. Also in my case principal is House and Dependent is Room. Do I need to add soemthing like "Optional Dependent" or "Optional Principal". Can some one please guide me I am a beginner.
Also, Do I need go remove navigation property from any of my MODELS? Is that extraneous?
How to tell EF to use HouseId of Room class as FK.
Also in my case principal is House and Dependent is Room
Then you are on the right track with
modelBuilder.Entity<House>()
.HasOptional(f => f.Room)
.WithRequired(s => s.House)
because in EF one-to-one relationship the required side is always the principal. You need to specify the principal/dependent only when both sides are required or both sides are optional.
The problem is your requirement of using different FK in the dependent entity than the PK, which is the default EF model for such relationship (so called Shared Primary Key Association), and is supported well. From the other side, one-to-one FK association is supported, but with some limitations. More specifically, explcit FK property like HouseId in your Room is not supported - there is no HasForeignKey fluent method for this type of configuration, and it's by purpose. I can't say why, but it's a fact. If you try playing with [ForeignKey] attribute, you'll get quite unexpected results, so don't do that.
Remove that property:
public class Room
{
public int RoomId { get; set; }
public string RoomName { get; set; }
public virtual House House { get; set; }
}
and use Map fluent API with MapKey to specify the FK column name:
modelBuilder.Entity<House>()
.HasOptional(f => f.Room)
.WithRequired(s => s.House)
.Map(c => c.MapKey("HouseId"));
This will give you the desired database table design (although there is no way to constrain HouseId column to be unique, except manually editing the migration).

C# Entity Framework Table Per Type adding unwanted column in base table

In C# I'm using Entity Framework and MVC and have:
public class Part
public class FinishedGoodsPart: Part
public int? ProductLineId { get; set; }
public class CompetitorPart : Part
public int? ProductLineId { get; set; }
public class OEPart: Part
public class ProductLine
public virtual ICollection<Part> Parts { get; set; }
Observe that FinishedGoodsPart and CompetitorPart have a ProductLine, but OEPart does not.
This puts a column called ProductLine_Id in the FinishedGoodsPart and CompetitorPart tables, as I expect and want, but also puts column ProductLine_Id in table Part (which I do not want).
How do I prevent E.F. from adding column Part.ProductLine_Id? Is there a better design?
The goal is that an instance of the ProductLine model object will have a list of all parts which belong to that product line (whether they are competitor or finished goods parts). But an OEPart is not allowed to have a ProductLine set.
FinishedGoodsPart objects will have a lot of attributes, whereas CompetitorPart will have very few, therefore I don't want to store all part records in a single database table.

Can I use a dictionary in a MVC Model?

Can I use something like:
public virtual Dictionary<User, double> Tenants { get; set; }
where what I want is a dictionary of Tenants in the way of:
User1, 25%
User2, 30%
User3, 45%
which says how much each person own from a House model (the virtual property above is in the House model).
How could I do this and is the current way of doing it correct?
As you mentioned entity-framework in your question tags then i can suppose what you try to map some relations to virtual IDictionary<T,T> in your data access class. But EF supports only things like this virtual IColletions<T>
But if this just a field for your view model which is separated from data access logic then it can be anything you want.
But maybe it will be bettor if you create something like
public Dictionary<int, CustomUserObjectWithShare> Tenants {get; set;}
public class CustomUserObjectWithShare
{
public User User { get; set;}
public Decimal Share { get; set; }
}

DAL, BLL and Web layer - create the same class for each layer? Is it correct?

For example: I need to display comments from Comments table in database.
So in DAL project I have POCO class:
public class Comments
{
public int CommentId { get; set; }
public string Author { get; set; }
public string Content { get; set; }
}
In BLL project I have class (DTO - data transfer object):
public class CommentsDTO
{
public int CommentId { get; set; }
public string Author { get; set; }
public string Content { get; set; }
}
So I get IEnumerable from DAL, convert to IEnumerable and return it to Web project.
In Web project I have class:
public class CommentsViewModel
{
public int CommentId { get; set; }
public string Author { get; set; }
public string Content { get; set; }
}
So in Web project I get IEnumerable from BLL, convert to IEnumerable and return it to view.
Is it correct? Because these classes have only different names.
It is certainly incorrect and you should not do this generally ad this just duplicates the code. You need different classes only when they differ. Suppose that you want to display a Comment in your web application and Comment is associated with User entity. Then, instead of sending 2 object separately you just create DTO class that combines properties of the two (like comment text and user name). Then sometimes you need to change the model in your web application to display data properly. For instance you want to display them in grid, and to do that properly you need to assign some attributes over properties and this is a good reason to create separate model in your web application. If you do not need to change your classes you should reuse existing ones. However even in your case changing CommentsViewModel a bit might be beneficial - usually you do not want user to see CommentId value (this is db internal thing) - so you can this field in [HiddenInput(DisplayValue = false)] so you could use Html.EditorForModel just to display editor panel. But this is your choice.
Creating each class for every layer is certainly beneficial for large projects. When I say large, I mean exceeding 5 developers with a application lifetime of 10 years. On large projects, the disadvantage of duplication becomes small compare to the benefit it provides.
For small projects, it is certainly overkill. The extra weight will slow you down with little or no benefit. Its like packing 7-day clothes on a 2 day hiking trip.
The larger the project, the more formal is your architecture.
With that said, here is my advice:
The Comment should be shared by the DAL and Business Layer.
If you don't have a "service layer", loose the DTO classes and map the Comment straight to the CommentViewModel. More about the service layer
I say Comment without "s" because only classes that represents collections should be named plural.

Resources