I want to load eagerly a class A instance with a certain Id. Then I want to .Include every class B instance related to A instance and .Include every C instance related to B instance.
This does not work:
var schoolyear = _context.Schoolyears
.Include(s => s.SchoolclassCodes)
.Include(s => s.TimeTableEntries)
.SingleOrDefault(s => s.Id == schoolyearId);
The last .Include is on the wrong level. It must relate to the .SchoolclassCodes.
Is this not possible with the stronly typed .Include() or do I have to use the weaked type style:
var schoolyear = _context.Schoolyears
.Include("SchoolclassCode.TimeTableEntries")
.SingleOrDefault(s => s.Id == schoolyearId);
class Schoolyear
{
public int Id { get; set;}
public ICollection<SchoolclassCode> {get;set;}
}
class SchoolclassCode
{
public ICollection<TimeTableEntry> {get;set;}
}
class TimeTableEntry
{
}
You can do it with the stongly typed method as well.
Just make a select query in the Include until you reach the last entity in the hierarchy that should be loaded:
var schoolyear = _context.Schoolyears
.Include(s => s.SchoolclassCodes
.Select(t => t.TimeTableEntries));
Related
Hi i just start working on a project. I saw a context query line. It includes entity then includes again.
I don't understand whats what mean. I created 2 diffrent var object. when removed the self include line then debugging. I saw same thing.
This is the code
using (var context = new DbContext())
{
var entity = context.Set<Book>()
..
...
.Include(x => x.Book)
.Include(x => x.BookAuthor).ThenInclude(x => x.Author)
..
... goes on
return entity.ToList()
}
I thought this is the same thing
using (var context = new DbContext())
{
var entity = context.Set<Book>()
..
...
//remove .Include(x => x.Book)
.Include(x => x.BookAuthor).ThenInclude(x => x.Author)
..
... goes on
return entity.ToList()
}
other example code is
.Include(x => x.CategoryBranches).ThenInclude(x => x.Category)
.Include(x => x.CategoryBranches).ThenInclude(x => x.Category).ThenInclude(x => x.BookCategories);
Can anyone explain the self include again. I saw it everywhere
Thank you
If the Book has a Navigation Property called Book (that doesnt make sense with that name semantically, but technically it does), you have to include it to load it.
You can have a reference to yourself, just like to any other table via a foreign key, and have a navigation property for that.
Consider this example that makes more sense semantically:
public class Person
{
public int Id { get; set; }
public int ParentId { get; set; }
public Person Parent { get; set; } // This is a self referencing navigation property
}
So now to use the parent you'd have to:
persons.Include(p => p.Parent). ... ;
to access the parent.
Lazy loading means related entities in a model class are loaded automatically.
The parent class provided by Mafii does not use lazy loading. That's why he had to use the Include method to load (or include) the parent entity (the related entity). If he didn't do it this way, any details under the Parent navigation will not be loaded. To load a related entity automatically, simply enable lazy loading by using the virtual keyword in the navigation property of the entity. This way, you don't have to use Include anymore.
public class Person
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Note: I think there is a performance downside to this. So, if you do this, you need to be sure you actually need those entities loaded.
I have stumbled upon an optimization issue and maybe there is a way around it.
Consider that I have the following Entity:
public class EntityOne
{
public int Id {get; set;}
public virtual ICollection<Names> Names { get; }
}
And I want to fetch a single Name that matches a specific criteria.
Lets say I create the given model:
public class EntityView
{
public int Id {get; set;}
public string SingleName {get; set;}
}
Now I want to fetch the information for the EntityView model in a single query.
This is how I'm doing this so far:
var result = db.EntityOnes
.Include(a => a.Names)
.Select(a => new EntityView
{
Id = a.Id,
Name = a.Names.Where(b => b.Criteria == Criteria).Select(b => b.Name).FirstOrDefault()
}).ToList();
Is there any way I could go about this in a way that I don't have to execute that FirstOrDefault inside of the main query, and still only fetch a single record for my EntityView model?
Thanks everyone!
I think this is faster than yours:
var result = db.EntityOnes
.Include(a => a.Names)
.Select(a => new EntityView
{
Id = a.Id,
SingleName = a.Names.FirstOrDefault(b => b.Criteria == Criteria)?.Name ?? ""
}).ToList();
You can skip Where() and Select() inside the query.
var result = db.EntityOnes
.Include(a => a.Names)
.Select(a => new EntityView
{
Id = a.Id,
SingleName = a.Names.FirstOrDefault(b => b.Criteria == Criteria)?.Name
}).ToList();
Knowing I would only have one result from my Names, I can just join the IEnumerable result as following:
var result = db.EntityOnes
.Include(a => a.Names)
.Select(a => new EntityView
{
Id = a.Id,
Name = string.Join("", a.Names.Where(b => b.Criteria == Criteria).Select(b => b.Name))
}).ToList();
This way I would get a single string out of my query and execute a single call to the database
I have a Category table and it has a Parent Category, I try to iterate over all the categories and get the parents categories with it's Inverse Parent but some of them returns without the inverse parents from unknown reason.
Categories.cs
public partial class Categories
{
public Categories()
{
InverseParent = new HashSet<Categories>();
}
public int Id { get; set; }
public int? ParentId { get; set; }
public DateTime CreateDate { get; set; }
public bool? Status { get; set; }
public virtual Categories Parent { get; set; }
public virtual ICollection<Categories> InverseParent { get; set; }
}
This is how I try to iterate them to create a select list items:
var parentCategories = await _context.Categories.
Include(x => x.Parent).
Where(x => x.Status == true).
Where(x => x.Parent != null).
Select(x => x.Parent).
Distinct().
ToListAsync();
foreach (var parent in parentCategories)
{
SelectListGroup group = new SelectListGroup() { Name = parent.Id.ToString() };
foreach (var category in parent.InverseParent)
{
categories.Add(new SelectListItem { Text = category.Id.ToString(), Value = category.Id.ToString(), Group = group });
}
}
So the problem is that some of my parent categories returns all their children categories and some don't and I don't why.
There are several issues with that code, all having some explaination in the Loading Related Data section of the documentation.
First, you didn't ask EF Core to include InverseParent, so it's more logically to expect it to be always null.
What you get is a result of the following Eager Loading behavior:
Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
Second, since the query is changing it's initial shape (Select, Disctinct), it's falling into Ignored Includes category.
With that being said, you should build the query other way around - starting directly with parent categories and including InverseParent:
var parentCategories = await _context.Categories
.Include(x => x.InverseParent)
.Where(x => x.InverseParent.Any(c => c.Status == true)) // to match your query filter
.ToListAsync();
While you are including Include(x => x.Parent), you don't seem to do the same for InverseParent. This might affect your results exactly the way you describe. Would including it fix it?
parentCategories = await _context.Categories.
Include(x => x.Parent).
Include(x => x.InverseParent).
Where(x => x.Status == true).
Where(x => x.Parent != null).
Select(x => x.Parent).
Distinct().
ToListAsync();
foreach (var parent in parentCategories)
{
SelectListGroup group = new SelectListGroup() { Name = parent.Id.ToString() };
foreach (var category in parent.InverseParent)
{
categories.Add(new SelectListItem { Text = category.Id.ToString(), Value = category.Id.ToString(), Group = group });
}
}
UPD: Since you are selecting x => x.Parent anyway it might be necessary to use ThenInclude() method instead.
In the entity framework, I have a main class with ICollection definitions for 2 sub classes.
public partial class Class1{
public virtual ICollection<Class2> Class2 {get;set;}
public virtual ICollection<Class3> Class3 {get;set;}
}
public partial class Class2 : ITotal {
public double Total {get;}
}
public partial class Class3 {
}
Class2 implements the ITotal interface...Class3 does not.
In total Class1 has about 30 instances of ICollections where the base object implements the ITotal interface. It also has 10+ ICollections that do not implement the interface.
Within Class1, I need to be able to dynamically get all ICollections whose base type implement the ITotal interface. Then I will add the "Total" fields to get an overall total. The reason I need it to be dynamic is because I will be adding many more ICollections to class1 and I don't want/need to have to remember to go to multiple places to get the totals to be accurate.
Below is a sample of what I have so far...this bit of code gives me all the ICollection classes, but now I am stuck. Ideally, I could add another where clause after the last select, but I am open to scrapping it altogether.
var value1 = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
.Where(x => x.CanWrite && x.GetGetMethod().IsVirtual)
.Select(x => x.GetValue(this, null))
.Where(x => x != null)
.Where(x => x.GetType().GetInterfaces().Any(y => y.IsGenericType && y.GetGenericTypeDefinition() == typeof(ICollection<>)))
.Select(x=> ((IEnumerable)x))
.ToList()
;
Any thoughts?
Like this:
var c = new Class1();
//. . .
var q = from p in typeof(Class1).GetProperties()
where p.PropertyType.IsConstructedGenericType
&& p.PropertyType.GetGenericTypeDefinition().Equals(typeof(ICollection<>))
&& typeof(ITotal).IsAssignableFrom(p.PropertyType.GetGenericArguments()[0])
select p;
var ITotalCollections = q.ToList();
var q2 = from p in ITotalCollections
from i in (IEnumerable<ITotal>)p.GetValue(c)
select i.Total;
var total = q2.Sum();
I have a Product object with a related Category within it.
I have the relationship between product and Category as a One to Many. But the Category can also be null.
Trouble is I cannot seem to handle null Category object.
I tried the following in my Product class:
private Category _category;
public virtual Category Category
{
get { return _category ?? (_category = new Category()); }
set { _category = value; }
}
And on my Database Context OnModelCreating method:
modelBuilder.Entity<Product>()
.HasRequired(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId);
Unfortunately on accessing the Product.Category in my design layer it always returns the New Category instance, rather than try to pull the Category by the Product.CategoryId (which does have a value).
How can I can setup my model to handle null Category?
If the Category is optional (it is because it can be null) you must use:
modelBuilder.Entity()
.HasOptional(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId);
And define your product as:
public class Product
{
...
public int? CategoryId { get; set; }
public virtual Category { get; set; }
}