EF Core 6 Many to many table after scaffold - .net-core

I've made a dotnet ef scaffold from database and the classes generated were:
public partial class Course
{
public int Id { get; set; }
public string? Description { get; set; }
}
public partial class Student
{
public int Id { get; set; }
public string? Name { get; set; }
}
public partial class StudentCourse
{
public int? IdStudent { get; set; }
public int? IdCourse { get; set; }
public virtual Student? IdStudentNavigation { get; set; }
public virtual Course? IdCourseNavigation { get; set; }
}
I want to get a List of Student where id of Course is X
I've tried _context.Student.Include("StudentCourse").Where(x=>x.Any(....) but Intellisense does not accept "Any" function.
How can i get this ?

Any(...) is a method provided by Enumerable class so you can not use it on a single Student (which is obviously not an Enumerable object).
Your configuration of many-to-many relationship is maybe missing some lines, here is my suggestion:
public partial class Course
{
public int Id { get; set; }
public string? Description { get; set; }
public List<StudentCourse> StudentCourses { get; set; }
}
public partial class Student
{
public int Id { get; set; }
public string? Name { get; set; }
public List<StudentCourse> StudentCourses { get; set; }
}
public partial class StudentCourse
{
public int? IdStudent { get; set; }
public int? IdCourse { get; set; }
public virtual Student? StudentNavigation { get; set; }
public virtual Course? CourseNavigation { get; set; }
}
In Context file:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentCourse>()
.HasOne(sc => sc.StudentNavigation)
.WithMany(s => s.StudentCourses)
.HasForeignKey(sc => sc.IdStudent);
modelBuilder.Entity<StudentCourse>()
.HasOne(sc => sc.CourseNavigation)
.WithMany(c => c.StudentCourses)
.HasForeignKey(sc => sc.IdCourse);
}
Finally, your query could be:
IEnumerable<Student> students = await _context.Students
.Include(s => s.StudentCourses)
.Where(s => s.StudentCourses.Any(sc => sc.IdCourse == X)))

I am just taking your code as example but this is not a way you design entity in EF core.
Try following though.
var students
=_context.StudentCourse.Include("IdStudentNavigation").Where(x=>x.IdCourse == 1).Select(x => x.IdStudentNavigation).ToList();
Replace one with your course id.

Related

Many-to-Many Relation only includes one Entity [duplicate]

This question already has an answer here:
Many to many relationship mapping in EF Core
(1 answer)
Closed 3 years ago.
Dotnet Core 2.2, EntityFrameworkCore 2.2.3
In a Many-to-Many relation between the entities "Post" and "Category" the linked Entity "PostCategory" returns the "Post" object but for the "Category" object only the Id and not the object itself.
Migrations and database update works fine and all three tables are created.
For the relation itself I tried it with EF "auto magic" and explicit definition of the relation in OnModelCreating in the ApplicationDbContext.
Models
Post-Model
public class Post
{
public int Id { get; set; }
public string Slug { get; set; }
public string Title { get; set; }
public string Abstract { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public DateTime Created { get; set; }
public ICollection<PostCategory> PostCategories { get; set; }
}
Category-Model
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<PostCategory> PostCategories { get; set; }
}
PostCategory Model
public class PostCategory
{
public int PostId { get; set; }
public Post Post { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
DbSets in ApplicationDbContext
public DbSet<Post> BlogPosts { get; set; }
public DbSet<Category> BlogCategories { get; set; }
public DbSet<PostCategory> PostCategories { get; set; }
Get all Posts from Service
public IEnumerable<Post> GetAll()
{
var posts = _context.BlogPosts
.Include(x => x.PostCategories);
return posts;
}
Calling service from Controller
public IActionResult Index()
{
var blogPosts2 = _blogService.GetAll();
...
}
The result is seen in the screenshot.
In ApplicationDbContext I tried two versions:
Version 1:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<PostCategory>()
.HasKey(x => new { x.PostId, x.CategoryId });
}
public DbSet<Post> BlogPosts { get; set; }
public DbSet<Category> BlogCategories { get; set; }
public DbSet<PostCategory> PostCategories { get; set; }
Version 2:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<PostCategory>()
.HasKey(x => new { x.PostId, x.CategoryId });
builder.Entity<PostCategory>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostCategories)
.HasForeignKey(pt => pt.PostId);
builder.Entity<PostCategory>()
.HasOne(pt => pt.Category)
.WithMany(t => t.PostCategories)
.HasForeignKey(pt => pt.CategoryId); ;
}
public DbSet<Post> BlogPosts { get; set; }
public DbSet<Category> BlogCategories { get; set; }
public DbSet<PostCategory> PostCategories { get; set; }
Both version migrate and update with no errors and the same result.
I'm grateful for any help.
Best regards
Edit:
I tried the "ThenInclude" before but obviously my Visual Studio auto completion has a problem:
If I ignore the auto completion, then it works, thank you!
To eager load related data in multiple level, you have to use .ThenInclude as follows:
public IEnumerable<Post> GetAll()
{
var posts = _context.BlogPosts
.Include(x => x.PostCategories)
.ThenInclude(pc => pc.Category);
return posts;
}
Here is the more details: Loading Related Data: Including multiple levels

ASP.NET Core 2.0 MVC EF One-to-Many and Many-to-Many relationship

In the last days I have been working on the creation of a relationship between different classes. Unfortunately, I can't solve it, hopefully you can help me further. Below you can find the problem definition.
I have the following classes:
BaseEntity
Product
Category
Tag
ProductCategory
ProductTag
BaseEntity Class:
public class BaseEntity
{
public long Id { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public BaseEntity { }
}
Product Class:
public class Product: BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public long CategoryId { get; set; }
public virtual Category MainCategory { get; set; }
public virtual ICollection<ProductSubCategory> ProductSubCategories { get; set; }
public virtual ICollection<ProductTag> ProductTags { get; set; }
}
Category Class:
public class Category : BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<ProductSubCategory> ProductSubCategories{ get; set; }
}
Tag Class:
public class Tag: BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<ProductTag> ProductTags { get; set; }
}
ProductSubCategory Class:
public class ProductSubCategory : BaseEntity
{
public long ProductId { get; set; }
public Product Product{ get; set; }
public long SubCategoryId { get; set; }
public Category Category { get; set; }
}
ProductTag Class:
public class ProductTag: BaseEntity
{
public long ProductId { get; set; }
public Product Product{ get; set; }
public long TagId { get; set; }
public Tag Tags { get; set; }
}
A product has one main category. (one-to-many)
A product could have many subcategories. (many-on-many)
A product could have many tags. (many-on-many)
My class-Maps are like this:
ProductMap:
public ProductMap(EntityTypeBuilder<Product> entityBuilder)
{
entityBuilder.HasOne(p => p.MainCategory)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId)
.OnDelete(DeleteBehavior.SetNull);
}
ProductSubCategoryMap:
public ProductSubCategoryMap(EntityTypeBuilder<ProductSubCategory> entityBuilder)
{
entityBuilder
.HasKey(psc=> new { psc.ProductId, psc.CategoryId });
entityBuilder
.HasOne<Product>(psc => psc.Product)
.WithMany(p => p.ProductSubCategories)
.HasForeignKey(psc => psc.ProductId);
entityBuilder
.HasOne<Category>(psc=> psc.Category)
.WithMany(sc => sc.ProductSubCategories)
.HasForeignKey(psc => psc.CategoryId);;
}
ProductTagMap:
public ProductTagMap(EntityTypeBuilder<ProductTag> entityBuilder)
{
entityBuilder
.HasKey(pt => new { pt.ProductId, pt.TagId});
entityBuilder
.HasOne<Product>(pt => pt.Product)
.WithMany(p => p.ProductTags)
.HasForeignKey(pt=> pt.ProductId);
entityBuilder
.HasOne<Tag>(pt => pt.Tag)
.WithMany(t => t.ProductTags)
.HasForeignKey(pt => pt.TagId);
}
How can I set:
One-to-many between Product(MainCategory) and Category.
Many-on-many between Product(SubCategories) and Category
Many-on-many between Product(Tags) and Tag.
Thanks in advance.
A few questions first, you are using code-first approach yes? As you are using the virtual navigation properties. Just want to be sure.
Are you currently getting any errors, what have you tried, what is failing?
Where you place the virtual property and not in the other model, that will give you a one-to-many relationship. If they are in both models you achieve the many-to- many relationship.
In Product class add
public virtual Catagory Catagory { get; set; }
If you need it to be a collection then add that before like you have in your others and maybe name the ICollection Catagories instead of singular .
public virtual ICollection<Catagory> Catagories { get; set; }

Many to many relation between Identity and custom table. EF7 - Code first

How can I make many to many relation between AspNetRoles from Identity 3.0 and my custom table? I want simple 3 table, with both PermissionId and RoleId, something like AspNetUsersRole. I have something like this:
public class Permission
{
public int PermissionId { get; set; }
public string Name { get; set; }
public virtual ICollection<ApplicationRole> Roles { get; set; }
}
public class ApplicationRole : IdentityRole
{
public virtual ICollection<Permission> Permissions { get; set; }
}
But when I want to add migration, I got error:
Unable to determine the relationship represented by navigation property 'ApplicationRole.Permissions' of type 'ICollection<Permission>'. Either manually configure the relationship, or ignore this property from the model.
EF Core (EF7) does not currently support many to many relationship without a join entity. (Reference)
So, what you should do is to create an entity class for the join table and mapping two separate one-to-many relationships. Like;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId });
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId);
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId);
}
public class PostTag
{
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Regarding to this question answer, it can be done more easily like this-
class Photo
{
public int Id { get; set; }
public ICollection<PersonPhoto> PersonPhotos{ get; set; }
}
class PersonPhoto
{
public int PhotoId { get; set; }
public Photo Photo { get; set; }
public int PersonId { get; set; }
public Person Person { get; set; }
}
class Person
{
public int Id { get; set; }
public ICollection<PersonPhoto> PersonPhotos{ get; set; }
}
Be sure to configure PersonPhoto with a composite key:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PersonPhoto>().HasKey(x => new { x.PhotoId, x.PersonId });
}
To navigate, use a Select:
// person.Photos
var photos = person.PersonPhotos.Select(c => c.Photo);
Add This namespace-
using Microsoft.AspNetCore.Identity;
public class Permission
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PermissionId { get; set; }
public string Name { get; set; }
public string UserIdFK { get; set; } //Foreign Key of Identity tbl
[ForeignKey("UserIdFK")]
public IdentityUser UserDetail { get; set; }
}
That's it, Happy coding :)

Foreign key relationship

I'm trying to setup a foreign key using the following two classes.
I want to use pAcqType as an enum and store the names of the types in another table. How should I setup my classes to do this?
public class Property
{
[Key]
public int pID { get; set; }
public string pAddress { get; set; }
public string pCounty { get; set; }
public string pCity { get; set; }
public string pState { get; set; }
public string pzip { get; set; }
public virtual PropertyAcquisitionType pAcqType { get; set; } <-- foreign key
}
public class PropertyAcquisitionType
{
[Key]
public int patID { get; set; }
public string patName { get; set; }
}
UPDATE
Dan got me thinking. And I tried the following and it seems to have worked out.
It setup the foreign key on the table like I wanted. And it didn't even ask for an inverse on the other table.
public int? pAcqType { get; set; }
[ForeignKey("pAcqType")]
public PropertyAcquisitionType patID { get; set; }
Is the foreign key required (NOT NULL in the database)?
public int pAcqTypeId { get; set; }
[ForeignKey("pAcqTypeId")]
public virtual PropertyAcquisitionType pAcqType { get; set; }
Otherwise,
public int? pAcqTypeId { get; set; }
[ForeignKey("pAcqTypeId")]
public virtual PropertyAcquisitionType pAcqType { get; set; }
Then in your other class, add an inverse relationship:
public class PropertyAcquisitionType
{
[Key]
public int patID { get; set; }
public string patName { get; set; }
[InverseProperty("pAcqType")]
public virtual ICollection<Property> pOfThisType { get; set; }
}
Here is one way you could define the relationship using the fluent API (without attributes in the entity classes). Note with this method, you should not need to add a properties property on the PropertyAcquisitionType entity to satisfy the inverse side of the relationship, because the .WithMany() tells EF what it needs to know:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Property>()
.HasKey(x => x.pID)
.HasRequired(x => x.pAcqType) // or HasOptional if using int?
.WithMany() // or WithMany(x => x.pOfThisType) if you want to add / keep the inverse property
.HasForeignKey(x => x.pAcqTypeId)
;
}
}

ASP.NET MVC & AutoMapper (Populate View Model from Parent & Child Domain Objects)

I have following doimain objects:
public class ComponentType
{
public int ComponentTypeID { get; set; }
public string Component_Type { get; set; }
public string ComponentDesc { get; set; }
}
public class AffiliateComponentType
{
public int AffiliateComponentID { get; set; }
public int AffiliateID { get; set; }
public ComponentType ComponentType { get; set; }
public bool MandatoryComponent { get; set; }
public bool CanBeBookedStandalone { get; set; }
public int PreferenceOrder { get; set; }
}
I will get a LIST of AffiliateComponentType from DB using NHibernate. Now I have to populate a LIST of AffiliateComponentTypeView (View Model) from LIST of AffiliateComponentType domain object. How can I achieve this using AutoMapper?
[Serializable]
public class AffiliateComponentTypeView
{
public int ComponentTypeID { get; set; }
public string Component_Type { get; set; }
public string ComponentDesc { get; set; }
public bool MandatoryComponent { get; set; }
public bool CanBeBookedStandalone { get; set; }
public int PreferenceOrder { get; set; }
}
The following mapping should do the job of flattening your model:
Mapper
.CreateMap<AffiliateComponentType, AffiliateComponentTypeView>()
.ForMember(
dest => dest.ComponentTypeID,
opt => opt.MapFrom(src => src.ComponentType.ComponentTypeID)
)
.ForMember(
dest => dest.Component_Type,
opt => opt.MapFrom(src => src.ComponentType.Component_Type)
)
.ForMember(
dest => dest.ComponentDesc,
opt => opt.MapFrom(src => src.ComponentType.ComponentDesc)
);
and if you modified your view model like this:
[Serializable]
public class AffiliateComponentTypeView
{
public int ComponentTypeComponentTypeID { get; set; }
public string ComponentTypeComponent_Type { get; set; }
public string ComponentTypeComponentDesc { get; set; }
public bool MandatoryComponent { get; set; }
public bool CanBeBookedStandalone { get; set; }
public int PreferenceOrder { get; set; }
}
The flattening will be performed automatically by AutoMapper using standard conventions so all you need is:
Mapper.CreateMap<AffiliateComponentType, AffiliateComponentTypeView>();
There will just be a slight problem with the Component_Type property as it clashes with AutoMapper's default naming convention so you might need to rename it.
Once you have the mapping defined you could map:
IEnumerable<AffiliateComponentType> source = ...
IEnumerable<AffiliateComponentTypeView> dest = Mapper.Map<IEnumerable<AffiliateComponentType>, IEnumerable<AffiliateComponentTypeView>>(source);
Somewhere in your app, you'll have a block of code that configures AutoMapper, so I'm guessing you'd have a block that looks like so:
Mapper.CreateMap<ComponentType, AffiliateComponentTypeView>();
Mapper.CreateMap<AffiliateComponentType, AffiliateComponentTypeView>();
Then, once you have your model back from nHibernate, you'll construct your view model like so:
var model = Session.Load<AffiliateComponentType>(id);
var viewModel = Mapper.Map<AffiliateComponentType,
AffiliateComponentTypeView>(model);
if (model.ComponentType != null)
Mapper.Map(model.ComponentType, viewModel);
Hope this gets you where you're headed!

Resources