Entity Framework Core 3.0 query not pulling the required information - entity-framework

I am trying to fetch data from two tables that has one to many relationship in Entity Framework Core 3. The models that represent the tables are shown below. One Person can have many PersonNotes. I need to query the PersonNotes table by passing the PersonId. I am currently getting an error saying Person does not contain the definition of Contains. How do I formulate this query.
class Person
{
[Key]
public int Id { get; set; }
public List<PersonNote> PersonNotes { get; set; }
}
class PersonNote
{
[Key]
public int Id { get; set; }
public int PersonId { get; set; }
}
class StackOverflow : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<PersonNote> PersonNotes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasMany(p => p.PersonNotes)
.WithOne()
.HasForeignKey(p => p.PersonId);
}
}
Query
public IEnumerable<PersonNote> GetPersonNotes(int personId)
{
var PersonNotes1 = PersonNotes.Where(p => Person.Contains(p.Id));
return PersonNotes1;
}

To get PersonNotes by one PersonID:
IQueryable<PersonNote> GetPersonNotesSingleId(int ID) =>
context.PersonNotes.Where(p => p.PersonId == ID);
To get PersonNotes by multiple PersonIDs:
IQueryable<PersonNote> GetPersonNotesMultipleId(IEnumerable<int> IDs) =>
context.PersonNotes.Where(p => IDs.Contains(p.PersonId));
The last query will generate IN T-SQL clause:
var notes = GetPersonNotesMultipleId(new[] { 1, 2, 3 });

Related

Entity Framework Core null relationship

I have created a simple EF Core to join two tables by using relationship (HasOne). But when I run it, the query only queries the master table (Employees) without joining to the second table (Contact) and it causes the model to not bind the data.
Could someone point out what I am missing in this code shown below? Thanks
public class Employees
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public Contact Contact { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string ContactNumber { get; set; }
public Employees Employee { get; set; }
public int EmployeeId { get; set; }
}
internal class EmployeeMap : IEntityTypeConfiguration<Employees>
{
public void Configure(EntityTypeBuilder<Employees> builder)
{
builder.HasKey(x => x.EmployeeId);
builder.Property(p => p.EmployeeId).ValueGeneratedOnAdd();
builder.HasOne(x => x.Contact).WithOne(y => y.Employee).HasForeignKey<Contact>(k => k.EmployeeId);
}
}
public class ContactMap : IEntityTypeConfiguration<Contact>
{
public void Configure(EntityTypeBuilder<Contact> builder)
{
builder.HasKey(x => x.Id);
builder.Property(p => p.Id).ValueGeneratedOnAdd();
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}
private EmployeeResponse GetEmployeeResponse()
{
var emp = _context.Employees.FirstOrDefault();
return new EmployeeResponse
{
ContactNumber = emp!.Contact.ContactNumber,
EmployeeId = emp.EmployeeId,
};
}
Solutions:
1. Enable lazy loading:
DbContext.Configuration.LazyLoadingEnabled = true;
2. Or load it manually with .Include:
_context.Employees.Include(x => x.Contact).FirstOrDefault();
More information about navigation propertys in ef.

Use EF Core 5 many-to-many relation with the "Join table" that has extra data

I would like to ask if anyone can help me with EF Core 5.
I have two tables that are in "many-to-many" relationship: on the Join table, in addition to the columns that act as foreign keys I also have other columns that I would like to map in EF Core.
The only solution I can think of is to create the relationship as it was done in EF Core 3, that is to use a "one to many" relationship with the join table.
Any suggestions?
Thanks.
Ps. Sorry for my english.
The only solution I can think of is to create the relationship as it was done in EF Core 3, that is to use a "one to many" relationship with the join table.
You can have your cake and eat it too.
EF Core 5 supports custom linking entities and using skip-level navigation at the same time.
There is an example in the docs where the linking entity is in the model, has additional properties, but the main entities skip over the linking entity with Collection Navigation Properties.
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
PostTag is a regular entity, and you can access it with db.Set<PostTag>() or from a Post or a Tag. Note the (optional) navigation properties from Post and Tag to PostTag.

How to create multiple Many-to-Many relationships using the same join table [EF7/Core]

Is it possible to create 2 M:M relationships using the same join table?
I have the following situation and am receiving the exception:
Unhandled Exception: System.InvalidOperationException: Cannot create a relationship between 'ApplicationUser.ExpertTags' and 'UserTag.User', because there already is a relationship between 'ApplicationUser.StudyTags' and 'UserTag.User'. Navigation properties can only participate in a single relationship
In Tag:
public class Tag {
public Tag() {
Users = new List<UserTag>();
}
public int TagId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<UserTag> Users { get; set; }
In ApplicationUser:
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
StudyTags = new HashSet<UserTag>();
ExpertTags = new HashSet<UserTag>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
public ICollection<UserTag> StudyTags { get; set; }
public ICollection<UserTag> ExpertTags { get; set; }
}
In UserTag (CLR join):
public class UserTag
{
public string UserId { get; set; }
public ApplicationUser User { get; set; }
public int TagId { get; set; }
public Tag Tag { get; set; }
}
In ApplicationDbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserTag>()
.HasKey(x => new { x.UserId, x.TagId });
modelBuilder.Entity<UserTag>()
.HasOne(ut => ut.User)
.WithMany(u => u.StudyTags)
.HasForeignKey(ut => ut.UserId);
modelBuilder.Entity<UserTag>()
.HasOne(ut => ut.User)
.WithMany(u => u.ExpertTags)
.HasForeignKey(ut => ut.UserId);
modelBuilder.Entity<UserTag>()
.HasOne(ut => ut.Tag)
.WithMany(t => t.Users)
.HasForeignKey(ut => ut.TagId);
}
Do I need to create separate CLR classes? Something like UserStudyTag and UserExpertTag?
Thanks!
Step down to SQL DB. You want to have table UserTag with one UserId field. How EF should guess, which records in this table are related to StudyTags and which to ExpertTags collections?
You should duplicate something.
Either split UserTag to two tables (UserStudyTag and UserExpertTag), or make two UserId fields in UserTag, say ExpertUserId and StudyUserId. Both nullable, with only one having some value in each record.

Query does not return child collections

I still struggle with this, why each of 'Category' items returns null 'Task' collections. I do have data in the database, what am I missing?
public class ApplicationUser : IdentityUser
{
public ICollection<Category> Categories { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public DateTime Timestamp { get; set; }
public ICollection<Task> Tasks { get; set; }
}
public class Task
{
public int TaskId { get; set; }
public string Name { get; set; }
public DateTime Timestamp { get; set; }
}
And here is the query:
public IEnumerable<Category> GetAllForUser(string name)
{
return _ctx.Users.Where(x => x.UserName == name)
.SelectMany(x => x.Categories)
.Include(x => x.Tasks).ToList();
}
Your query is falling into Ignored Includes case:
If you change the query so that it no longer returns instances of the entity type that the query began with, then the include operators are ignored.
As explained in the link, if you add the following to your DbContext OnConfiguring:
optionsBuilder.ConfigureWarnings(warnings => warnings.Throw(CoreEventId.IncludeIgnoredWarning));
then instead null collection you'll get InvalidOperationException containing something like this inside the error message:
The Include operation for navigation: 'x.Tasks' was ignored because the target navigation is not reachable in the final query results.
So how to fix that? Apparently the requirement is to start the query from the entity for which you want to add includes. In your case, you should start from _ctx.Categories. But in order to apply the same filter, you need to add the reverse navigation property of the Application.Users to the Category class:
public class Category
{
// ...
public ApplicationUser ApplicationUser { get; set; }
}
Now the following will work:
public IEnumerable<Category> GetAllForUser(string name)
{
return _ctx.Categories
.Where(c => c.ApplicationUser.UserName == name)
.Include(c => c.Tasks)
.ToList();
}
Try this:
public IEnumerable<Category> GetAllForUser(string name)
{
return _ctx.Users
.Include(u => u.Categories)
.Include(u => u.Categories.Select(c => c.Tasks))
.Where(x => x.UserName == name)
.SelectMany(x => x.Categories)
.ToList();
}
public virtual ICollection<Task> Tasks { get; set; }

configure many to many relationship in entity framework

So following this example:
http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
I have a Student class:
public class Student
{
public Student() { }
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
and a Course class
public class Course
{
public Course()
{
this.Students = new HashSet<Student>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
They have Many-to-Many relationship which is configured this way:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasMany<Course>(s => s.Courses).WithMany(c => c.Students).Map(c =>
{
c.MapLeftKey("Student_id");
c.MapRightKey("Course_id");
c.ToTable("StudentAndCourse");
});
base.OnModelCreating(modelBuilder);
}
I have created the models and tables myself and map to them. So far so good. The problem is I do not need the collection of related Students to my courses. In other words when I get my courses they are coming with their related Students. I need the Students with the assigned Courses but when I get the courses through my repo, I need only them.
I tried to remove the Students collection from my Course class but was enable to fix the mapping. I am not experienced with EF and any help with working example will be greatly appreciated.
There is WithMany() method which does not require navigation property for related entities:
modelBuilder.Entity<Student>()
.HasMany(s => s.Courses)
.WithMany()
.Map(c =>
{
c.MapLeftKey("Student_id");
c.MapRightKey("Course_id");
c.ToTable("StudentAndCourse");
});