Query does not return child collections - entity-framework

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; }

Related

Include after select not load navigation property in Entity Framework core

I use entity framework core 1.1.
I have a query like below, and I expect to users who have UserProfile by using Include, load UserProfile.
But this query always return UserProfile null .
Query:
var user = dbContext.UserMappers
.Where(e => e.OldUserId == id)
.Select(e => e.User)
.Include(e=>e.UserProfile)
.FirstOrDefault();
Models:
public class UserMapper
{
[Key, ForeignKey(nameof(User))]
public string UserId { get; set; }
public User User { get; set; }
public int OldUserId { get; set; }
}
public class User : IdentityUser
{
public bool Suspended { get; set; }
public string Nickname { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
public class UserProfile
{
[Key, ForeignKey(nameof(User))]
public string UserId { get; set; }
public string Name { get; set; }
public string Family { get; set; }
public string Telephone { get; set; }
}
From the EF Core documentation - Loading Related Data - Ignored includes section (highlight is mine):
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.
This is different from EF6 where Include works on the final query entity type. I don't know if this is a current limitation or "by design", but for now you have to start your queries with the entity requiring includes.
In your case, it should be something like this:
var user = dbContext.Users
// if you don't have inverse navigation property
.Where(e => dbContext.UserMappers.Any(um => um.UserId == e.Id && um.OldUserId == id))
// if you have inverse collection navigation property
//.Where(e => e.UserMappers.Any(um.OldUserId == id))
// if you have inverse reference navigation property
//.Where(e => e.UserMapper.OldUserId == id)
.Include(e => e.UserProfile)
.FirstOrDefault();

Joining data with Entity framework

I'm trying to retrieve all employments linked to employees with a given UserID + all employments linked to managers with a given UserID. I'm joining the sets of data the way SQL UNION would do. This is the code I wrote for this purpose, but it seems that INCLUDEs are cumulative (the second works on the set of data retrieved by the first), which is not my intention (the second set of data has to add to the first):
var list = await db.Employment.AsNoTracking()
.Where(x => x.Active)
.Include(x => x.Employee).Where(x => x.Employee.UserID == UserID)
.Include(x => x.Manager).Where(x => x.Manager.UserID == UserID)
.ToListAsync();
Employment model
public class Employment : IHasID, IValidatableObject
{
public int ID { get; set; }
public int EmployeeID { get; set; }
public Employee Employee { get; set; }
public bool Active { get; set; }
public int? ManagerID { get; set; }
public Employee Manager { get; set; }
}
Employee model
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int? UserID { get; set; }
public User User { get; set; }
public bool Active { get; set; }
[InverseProperty("Employee")]
public List<Employment> EmploymentEmployee { get; set; }
[InverseProperty("Manager")]
public List<Employment> EmploymentManager { get; set; }
}
Later, I discovered that there's a Union extension, so I tried to use it:
var list = await
db.Employment.Where(x => x.Active).Include(x => x.Employee)
.Include(x => x.Manager).Where(x => x.Employee.UserID == UserID)
.Union(
db.Employment.Where(x => x.Active).Include(x => x.Employee)
.Include(x => x.Manager).Where(x => x.Manager.UserID == UserID))
.ToListAsync();
...but this results in an exception: InvalidOperationException: Warning as error exception for warning 'CoreEventId.IncludeIgnoredWarning': The Include operation for navigation: 'x.Employee' was ignored because the target navigation is not reachable in the final query results.

How to filter child collections Entity Framework

WEB API
Model::
public class Empresa
{
[Key]
public string CDEmpresa { get; set; }
public string NomeFantasia { get; set; }
[IgnoreDataMember]
public string Nome{ get; set; }
public List<EmpresaRamoAtividade> EmpresaRamoAtividade { get; set; }
}
public class EmpresaRamoAtividade
{
[Key]
public int CTRamoAtividade { get; set; }
[IgnoreDataMember]
public string CDEmpresa { get; set; }
public List<RamoAtividade> RamoAtividade { get; set; }
}
public class RamoAtividade
{
[IgnoreDataMember]
[Key]
public int CTRamoAtividadeTraducao { get; set; }
public int CTRamoAtividade { get; set; }
public string Atividade { get; set; }
public int Idioma { get; set; }
}
Controller::
Working ok:::
{
return db.Empresas
.Where(a => a.Associado.IsAssociado)
.Include(empresaRamo => empresaRamo.EmpresaRamoAtividade)
.Include(ramo => ramo.EmpresaRamoAtividade.Select(atividade => atividade.RamoAtividade));
}
Not working, I have to filter by "idioma" (language):::
{
return db.Empresas
.Where(a => a.Associado.IsAssociado)
.Include(empresaRamo => empresaRamo.EmpresaRamoAtividade)
.Include(ramo => ramo.EmpresaRamoAtividade.Select(atividade => atividade.RamoAtividade.Where(idioma => idioma.Idioma == 1)));
}
Error:
The Include path expression must refer to a navigation property
defined on the type. Use dotted paths for reference navigation
properties and the Select operator for collection navigation
properties. Parameter name: path
Can't I filter 3 level collection child?
Thank you..
You cannot filter using Include methods. It only supports select.
Disclaimer: I'm the owner of the project Entity Framework Plus (EF+)
EF+ Query IncludeFilter allow you to easily filter related entities:
{
return db.Empresas
.Where(a => a.Associado.IsAssociado)
.IncludeFilter(empresaRamo => empresaRamo.EmpresaRamoAtividade)
.IncludeFilter(ramo => ramo.EmpresaRamoAtividade.Select(atividade => atividade.RamoAtividade.Where(idioma => idioma.Idioma == 1)));
}
You can find the documentation here

Entity Framework Core (EF 7) many-to-many results always null

I have followed the instructions for the workaround for many-to-many described in Issue #1368 and the Docs Here... but when I try to navigate, it always returns null.
My Models:
public class Organization
{
public Guid OrganizationID { get; set; }
//...
public ICollection<OrganizationSubscriptionPlan> OrganizationSubscriptionPlans { get; set; }
}
public class SubscriptionPlan
{
public int SubscriptionPlanID { get; set; }
//...
public ICollection<OrganizationSubscriptionPlan> OrganizationSubscriptionPlans { get; set; }
public class OrganizationSubscriptionPlan
{
[ForeignKey("Organization")]
public Guid OrganizationID { get; set; }
public Organization Organization { get; set; }
[ForeignKey("SubscriptionPlan")]
public int SubscriptionPlanID { get; set; }
public SubscriptionPlan SubscriptionPlan { get; set; }
}
ApplicationDbContext:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<OrganizationSubscriptionPlan>().HasKey(x => new { x.OrganizationID, x.SubscriptionPlanID });
builder.Entity<OrganizationSubscriptionPlan>().HasOne(x => x.Organization).WithMany(x => x.OrganizationSubscriptionPlans).HasForeignKey(x => x.OrganizationID);
builder.Entity<OrganizationSubscriptionPlan>().HasOne(x => x.SubscriptionPlan).WithMany(x => x.OrganizationSubscriptionPlans).HasForeignKey(x => x.SubscriptionPlanID);
}
And my Query:
var organizations = _context.Organizations
.Include(o => o.OrganizationSubscriptionPlans);
foreach (var organization in organizations)
{
//....
var subscriptions = organization.OrganizationSubscriptionPlans
.Select(s => s.SubscriptionPlan);
// ^^^^^^^^^^^ why is subscriptions always null?
}
The "organizations" query returns the results as expected, including the list of OrganizationSubscriptionPlans within each one, but when I try to navigate to them in the foreach loop the "subscriptions" query returns null every time. What am I doing wrong?
Turns out it's a Lazy Loading issue. You have to "Include" the joining entity and then "ThenInclude" the other entity.
var organizations = _context.Organizations
.Include(o => o.OrganizationSubscriptionPlans)
.ThenInclude(s => s.SubscriptionPlan);
ForeignKey attr is to decorate reference properties to indicate them what primitive property hold the FK value.
public class OrganizationSubscriptionPlan
{
public Guid OrganizationID { get; set; }
[ForeignKey("OrganizationID")]
public Organization Organization { get; set; }
public int SubscriptionPlanID { get; set; }
[ForeignKey("SubscriptionPlanID")]
public SubscriptionPlan SubscriptionPlan { get; set; }
}

Why can't I do ToList()?

I build a model as below. The relationship between Recycler and Account is 1:1.
public class MyContext : DbContext
{
public DbSet<Quoter> Quoters { get; set; }
public DbSet<Account> Accounts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>()
.HasRequired(a => a.RecyclerRef)
.WithRequiredDependent(r => r.AccountRef);
}
}
public class Quoter
{
public int QuoterId { get; set; }
public string Text { get; set; }
}
public class Recycler : Quoter
{
public string Description { get; set; }
public virtual Account AccountRef { get; set; }
}
public class Account
{
public int AccountId { get; set; }
public Recycler RecyclerRef { get; set; }
}
But, I get exceptions when I do the either of these queries:
var query1 = context.Quoters
.OfType<Recycler>()
.Include(r => r.AccountRef)
.Where(r => r.QuoterId == 1)
.ToList();
var query2 = context.Set<Recycler>()
.Include(r => r.AccountRef)
.Where(r => r.QuoterId == 1)
.ToList();
Exception shows that ResultType is “Transient.reference[POCOFirst.Quoter]”,but recommanded is “Transient.reference[POCOFirst.Recycler]”
If I remove the ToList(), it works well. But I need a list as the return value of method.
Why can't I do ToList()? Thanks
It looks like you have stumble upon this bug in EF. Another reference to the bug.
Workaround would be to remove the Include method.