Use GetAllIncluding to get multiple levels in a single call - entity-framework

Using ABP Repository pattern we are trying to create a single query to retrieve a set of entities, along with their children, and the childrens children
Entity X -> one to many Entity Y -> one to many Entity Z
(think Invoice > InvoiceItem > InvoiceItemParts for example)
The Abp repository pattern provides for retrieving at least 1 set of children using
result = _repositoryInvoice.GetAllIncluding(x => x.InvoiceItem)
is there a way using LINQ to include InvoiceItemParts in this 1 query? If not, what is the recommended way to retrieve all child nav properties and all levels using a single call.
The main goal is making it so we don't have to make multiple round trips to the DB when accessing the child properties along with the child properties of those children.
thanks
jasen

Decided to just create my own custom repository and use EFCore .Include and .ThenInclude directly. Turns out AspNetBoilerPlate has no support for querying navigation properties beyond their immediate children of the root entity being queried.
Microsoft provides some great examples of using .Include and .ThenInclude:
https://learn.microsoft.com/en-us/ef/core/querying/related-data
Good luck!

GetAllIncluding accepts params, so you can specify multiple attributes:
var result = await _repositoryInvoice
.GetAllIncluding(x => x.InvoiceItem,
x => x.InvoiceItemParts)
.ToListAsync();

With IRepository<> there are the method Include and ThenInclude, where you can use the same structure of EF Core standard.
_repository.GetAll().Include(d => d.Child).ThenInclude(c => c.GrandChild).FirstOrDefault(x => x.Id.Equals(id));

Related

How can I recursively retrieve a hierarchy of records using EF Core?

I am using EF Core 2.1. I have a Group object that has the following properties:
int GroupId
int? ParentGroupId
Group ParentGroup
The object references the GroupId of its parent using the ParentGroupId property. The depth of the hierarchy is not known when querying. How can I retrieve the entire hierarchy?
I've tried the following, which will get me three levels deep, but how can I get all levels of the hierarchy without knowing the depth? Do I need to rely on a stored proc?
var group = await _membershipDbContext.Groups
.Include(g => g.ParentGroup)
.ThenInclude(g => g.ParentGroup)
.SingleOrDefaultAsync(g => g.GroupId == id);
You have three options:
Use lazy loading. This way every time you'll access the parent group, it will automatically loaded from the database.
Use explicit loading for recursively load the parent groups (see explicit loading)
Create a view or stored procedure in the database that will return all the data you need and then create the object graph manually.
Which method is right for you depends on your unique use-case and the amount of data to load.
Options 1 and 2 are not that different from each other, but with option 2 you control when the objects are loaded. Option 3 will probably be the most efficient, but might pose problems when saving back changes (as you transform the object graph manually).

How to model Database where a table entry can refer to another entry in the same table

I've a web app, being used by engineers for Asset(machines, scales) calibrations on a Site & certificate can be generated based on those readings. Up till now, the requirement was so that for a given SITE includes multiple ASSET and each asset has its own CALIBRATION.
So had my Modal like this.
With a recent change to include a new type of certificate. Where an ASSET can have multiple calibration (Two to be exact) one before Adjustment and One after if needed.
My question is, what is the best way to accommodate this change? Should I change the relation between ASSET one-to-one CALIBRATION to one-to-many with multiplicity (1..2) which basically requires to change lot of code check. or should adding another column in ASSET table which points to another entry within the same table. Or is there any other approach to opt ?
I'm using ASP.MVC, with Entity Framework.
Mapping
public Report_AssetMap()
{
HasKey(one => one.report_asset_id);
// Site_Report one-to-many-rel Report_Asset
HasRequired(one => one.Site_Report).WithMany(one => one.Report_Assets).HasForeignKey(one => one.site_report_id);
// Report_Asset one-to-one-rel Asset_Calcert
HasOptional(one => one.Asset_Calcert).WithRequired(ad => ad.Report_Asset).WillCascadeOnDelete(true);
}
public Asset_CalcertMap()
{
HasKey(one => one.report_asset_id);
// User one-to-many Asset_Calcert (with nullable Calcert_handled_by_id at many End)
HasOptional(o => o.Calcert_Handled_By).WithMany(r => r.Handled_Calcert).HasForeignKey(o => o.calcert_handled_by_id);
}
I would have a separate table for each of the pre and post adjustment certs; this is a solid use case for table-per-concrete-class inheritance (which is not yet included in EF Core but I'm guessing you're using EF6). Put common properties in a base class, derive a class for your pre and post adjustment certs (can be empty if no differentiation other than table names), then MapInheritedProperties and specify different table names for the derived classes in the model configuration.
https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-3-table-per-concrete-type-tpc-and-choosing-strategy-guidelines
If you then want to query against all Asset_CalCerts, you can specify a single DbSet<Asset_Caclert> DbSet in the DbContext to query while your Report_Asset entity can have a property referencing each derived Asset_CalCert type.
In this way you can keep your 1:? relationship while maintaining the ability to query all Asset_CalCerts as though they were in a single table.

Select only specific columns from included table in Entity Framework Core

If I have this
var selectedEntities = db.MyEntities.Include(item => item.RelatedEntities);
It will load all properties (columns) in MyEntities but also all properties in ReleatedEntities. If I only need one property from RelatedEntities, how would I specify that?
use .Select() and anonymous type to restrict the columns you want
var result = await _appDbContext.Companies
.AsNoTracking()
.Select(company => new
{
Company = company,
EmployeeIds = company.Employees.Select(emp => emp.Id)
})
.ToListAsync()
.ConfigureAwait(false);
I was looking for the same, and after referring to other questions it seems like it's not possible. The understanding i got is that an EF entity is represented by the collection of it's properties, and hence loading individual values will not fully define the Entity.
To load only selected properties, we need to use Select query, instead of loading the related data. The query will result in an anonymous type.
Note : If the resulting type contains any entity, then the changes will be tracked. Refer Tracking and projections
References :
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/basic-linq-query-operations#selecting-projections
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/projection-operations
How to include only selected properties on related entities
https://learn.microsoft.com/en-us/ef/core/querying/tracking#tracking-and-projections

Finding an entity then attaching relational data vs. using Include and then FirstOrDefault

Considering a table [Person] which has two foreign keys ([Phone_Numbers], [Business_Information]). When using EF Core, we can simply find a Person using the dbContext.Find method like var person = await db.Context.FindAsync<Person>(1); however, the Find method only looks for the entity inside the tracking cache and it does not handle relational properties. In order to solve this, we can call the Entry method to attach those properties like dbContext.Entry<Person>(person).Reference(x=> x.Business_Information). Considering the provided example, we have to call the Entry method twice in this case:
dbContext.Entry<Person>(person).Reference(x=> x.Business_Information).Load();
dbContext.Entry<Person>(person).Collection(x=> x.Phone_Numbers).Load();
An alternative solution is to use the Include method:
var person = await dbContext.Set<Person>().Include("Business_Information").Include("Phone_Numbers").FirstOrDefaultAsync(x=> x.id == id);
The first solution sends two request to the Db (I think the Find method does not send a request if the entity is being tracked); however, I'm not sure how the second one works and accordingly I'm also unsure if it has any performance advantages. I've been thinking the first solution could be faster and more efficient. I'd appreciate if someone clarifies this for me.
It really depends on number of related properties, their type (reference or collection) and in the first case - if they are already loaded or not.
Let say your entity has N reference navigation properties and M collection navigation properties that you want to load.
The approach with Include will always execute 1 + M db queries - one for the entity + reference properties which data is retrieved with JOINs to the corresponding tables and returned as columns in the query result) and one for each collection - regardless of whether the entity and any of the related entities/collections is already loaded.
The approach with explicit loading is more dynamic.
It will execute 1 db query for the entity if it's not loaded in the context, 0 otherwise.
For each reference navigation property it will execute 1 db query if the referenced entity is not already loaded in the context, 0 otherwise.
For each collection navigation property, it will execute 1 db query if the collection is not marked as loaded (db.Entry(entity).Collection(e => e.Collection).IsLoaded == false), 0 otherwise.
At the end, the explicit loading approach could execute between 0 and 1 + N + M db queries.
With all that being said, it's not clear which one is better. If you are using relatively short lived DbContext instances, hence the chances of not execution related queries is low, I would go with Include approach because it is deterministic.

DDD and Entity Framework - manually building associations

If we manually build our associations will we have to expose foreign keys in our domain model?
For example, if I a retrieve all Products and all Categories, the only way I can manually build the Product.Categories property is if I expose the ProductCategory mapping in my model?
I'd rather not do this if there is an alternative (note that eager loading via EF is not an option as I have two many associations to load).
You could add a partial class "Product" to your project and extend your "Product" entity with an IEnumerable<Category> property "Categories" (Or even a method that returns IEnumerable<Category>).
This way you could implement the retrieval of "Categories" yourself.
The solution was to use a Linq projection to retrieve the relevant keys.
var tags = (from p in postRepo.GetAll()
from t in p.Tags
select new
{
PostId = p.Id,
Tag = t
}).Take(1000).ToList();
I stick the results of the above query into a List<KeyValuePair<Guid, Tag>> which is then cached. When I load a Post I can manually build it's "Tags" collection from the cache.
This is an approach that we have had to take with a number of associations now. Whilst lazy loading is very convenient, IMHO it should not be used with your Views.
By caching common associations using the above method, we were able to reduce a page previously issuing 59 queries (as a result of lazy loading) to just 6 queries.