So I'm trying to include multiple nested properties from a child with LINQ
var templates = context.Templates
.Include(t => t.template_fields)
.Include(t => t.templateinstances.Select(ti => ti.templateinstance_fields))
.Include(t => t.templateinstances.Select(ti => ti.templateinstance_categories.Select(tic => tic.category)))
.ToList();
But when I include t.templateinstances more than once, I get a NullPointerException when the ToList() call is made.
There is no problem if t.templateinstances is only included once.
You should check possible null properties. Perhabs it should be;
var templates = context.Templates
.Include(t => t.template_fields)
.Include(t => t.templateinstances.Where(ti => ti != null).Select(ti => ti.templateinstance_fields))
.Include(t => t.templateinstances.Where(ti => ti != null && ti.templateinstance_categories != null).Select(ti => ti.templateinstance_categories.Select(tic => tic.category)))
.ToList();
For EF 6
using System.Data.Entity;
query.Include(x => x.Collection.Select(y => y.Property))
See Remarks for more examples.
Make sure to add using System.Data.Entity; to get the version of Include that takes in a lambda.
If you are using EF Core you can use the new method ThenInclude
query.Include(x => x.Collection)
.ThenInclude(x => x.Property);
Related
I have this linq query which retrieves all element in a database, and filters the output based on some criteria..
I want a list of certain entities, with only attributes that conforms to a specific filter
var entitiesWithLookupsTolookupEntityName = schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.Take(1)
.Include(x => x.Entities)
.ThenInclude(x => x.Attributes)
.ThenInclude(x => x.AttributeTypeSpecification)
.SelectMany(x => x.Entities
.Where(x => x.Attributes.Any(y => y.Type == DataType.Lookup && y.AttributeTypeSpecification.EntityInternalName == lookupEntityName))).AsEnumerable();
This returns the only entity which conforms to the filter, but also includes all the attributes of the entity which does not conform to the filter.
I can though in a second query like this filter them away as such
var attributefiltered = entitiesWithLookupsTolookupEntityName.SelectMany(x =>
x.Attributes.Where(y =>
y.Type == DataType.Lookup && y.AttributeTypeSpecification.EntityInternalName == lookupEntityName));
but why can't I combine both of these?
Seems weird that I am able to do it two steps but not one?
Something wrong with using .where() .any()
I'm trying to improve the performance of a query that uses linq and fluent API.
The query looks like this:
var result = DbContext.Set<Parent>()
.Join(DbContext.Set<Child>(),
(parent) => parent.Id,
(child) => child.ParentId,
(parent, child) => cild)
.Select(x => new { x.SomeOtherId, x.AnotherId })
.Where(x => !filters.Contains(x.SomeOtherId))
.Select(x => x.AnotherId )
.Distinct()
.ToListAsync(cancellationToken);
As I understand, .Contains degrades the performance of queries such as this and using a Join is better, e.g
.Join(filters, (c) => c.SomeOtherId, (filterId) => filterId, (c, filterId) => c.AnotherId);
This will give results where there is a match, however how would I find results where there is not a match?
Thanks
I want to simplify a linq Query that contains multiple includes.
My model is simple: a site is linked to one contract, that is linked to one client. On that client I need to get with a single request the telephones, mails and honorifics (appelRef).
I want a single request because behind the request is translated by entity framework into a SQL Server request.
Here is the linq request:
var search =
from IMT.Site s in imtContext.IMTObjects.OfType<IMT.Site>()
.Include(
s => s.LienContratSiteRef
.Select(l => l.Contrat)
.Select(c => c.LienContratClientRef
.Select(l => l.Client)
.Select(cl => cl.Telephones ) ) )
.Include(s => s.LienContratSiteRef
.Select(l => l.Contrat)
.Select(c => c.LienContratClientRef
.Select(l => l.Client)
.Select(cl => cl.Mails ) ) )
.Include(s => s.LienContratSiteRef
.Select(l => l.Contrat)
.Select(c => c.LienContratClientRef
.Select(l => l.Client)
.Select(cl => cl.AppelRef ) ) )
where s.Reference.ToString() == siteId
select s;
Yor can notice the block
.Include(
s => s.LienContratSiteRef
.Select(l => l.Contrat)
.Select(c => c.LienContratClientRef
.Select(l => l.Client)
..is repeated three time. Is ther a way to factorize that code block ?
Update: there are intermedray objects LienContratSiteRef and LienContratClientRef and relationships are 0 - *, so that LienContratSiteRef.Contrat and LienContratClientRef.Client are collections.
I also tried:
.Include(
s => s.LienContratSiteRef
.Select(l => l.Contrat)
.Select(c => c.LienContratClientRef
.Select(l => l.Client)
.Select(cl => new { Tels = cl.Telephones, Mail = cl.Mails, Appel = cl.AppelRef} ) ) )
but It results with a runtime error:
The Include path expression must refer to a navigation property
defined on the type.
The s => ... inside the include could be refactored into a delegate, then just .Include that delegate multiple times.
It looks like the signature would be Func<Site, IEnumerable<Client>>?
E.g.
static IEnumerable<Client> Foo(Site site) => site.LienContratSiteRef
.Select(l => l.Contrat)
.Select(c => c.LienContratClientRef
.Select(l => l.Client)
String-based Chaining
The Include() method supports a dot-delimited string parameter which can be used to pull an complete graph of objects down as opposed to making multiple chained select calls:
.Include("LienContratSiteRif.Contrat.LienContratClientRef.Client")
From there, if you wanted to include multiple additional properties, I believe that you could have another include for those sub properties:
.Include("LienContratSiteRif.Contrat.LienContratClientRef.Client, Client.Telephones, ...")
Lambda-based Chaining
You should be able to accomplish something similar by chaining your lambda-based includes into a single Include() call as well using:
.Include(c => LienContratSiteRif.Contrat.LienContratClientRef.Client)
It looks like you're trying to make an entity framework projection!
A project allows you to select only the properties that you want to return. (just like a SQL Select)
To use a projection, your code should roughly look like this:
var search = imtContext.IMTObjects.OfType<IMT.Site>()
.Where(s => s.Reference.ToString() == siteId)
.Select(s => new {
Telephones = s.LienContratSiteRef.Contrat.Select(c => c.LienContratClientRef.Client.Select(cli => cli.Telephones),
Mails = s.LienContratSiteRef.Contrat.Select(c => c.LienContratClientRef.Client.Select(cli => cli.Mails),
AppelRef = s.LienContratSiteRef.Contrat.Select(c => c.LienContratClientRef.Client.Select(cli => cli.AppelRef)
}).ToList();
If Telephones, Mails, AppelRefare also collections, then you can do aggregate the collections together like this in memory after the query has been run:
var telephones = search.SelectMany(x => x.Telephones).ToList();
var mails = search.SelectMany(x => x.Mails).ToList();
var appelRefs = search.SelectMany(x => x.AppelRef).ToList();
I have to load an entity from the database and I need to eagerly load into that entity almost all the relations from the database. I tried using Include but without success. Here is the code:
var thingy = _ctx.MASTERANDCOMMANDER
.Include(x => x.RED)
.Include(x => x.RED.Select(y => y.RED_ONE))
//.Include(x => x.RED.Select(y => y.RED_TWO))
.Include(x => x.GREEN)
.Include(x => x.GREEN.Select(y => y.GREEN_ONE))
.Include(x => x.GREEN.Select(y => y.GREEN_ONE.Select(z => z.GREEN_ONE_BIG)))
//.Include(x => x.GREEN.Select(y => y.GREEN_TWO))
//.Include(x => x.GREEN.Select(y => y.GREEN_THREE))
.SingleOrDefault(x => x.ID == "someId");
If I uncomment any of those includes, the app throws:
System.Exception: Oracle 11.2.0.2.0 doesn't support APPLY
Using EntityFramework 6 and Oracle 11.2.0.2.0.
I cannot upgrade EF nor Oracle.
How can I load the relations from GREEN_TWO, etc. into thingy?
Edit: All relations shown are one to many from left to right. Examples:
RED (1 to *) RED_ONE
GREEN_ONE (1 to *) GREEN_ONE_BIG
You can always use Join instead of Include, e. g.:
var thingy = _ctx.MASTERANDCOMMANDER
.Join(RED, x => x.MASTERANDCOMMANDERID, x => REDID, (m, r) => m)...etc...
and so on, where you need to get RED from your database as another single entity.
maybe using .ThenInclude() would help
var thingy = _ctx.MASTERANDCOMMANDER
.Include(x => x.RED)
.ThenInclude(red => red.RED_ONE)
.Include(x => x.GREEN)
.ThenInclude(green => green.GREEN_ONE)
.ThenInclude(greenOne => greenOne.GREEN_ONE_BIG)
.Where(x => x.ID == "someId").SingleOrDefault();
I know that this won't work as written, but I'm struggling to see the right answer, and this non-functional code hopefully illustrates what I'm trying to achieve:
var defaults = _cilQueryContext.DefaultCharges
.Where(dc => dc.ChargingSchedule_RowId == cs.RowId);
List<DevelopmentType> devTypes =
defaults.Select(dc => dc.DevelopmentType)
.Include(d => d.DefaultCharges)
.Include(d => d.OverrideCharges.Where(oc => oc.ChargingSchedule_RowId == cs.RowId))
.Include(d => d.OverrideCharges.Select(o => o.Zone))
.ToList();
Essentially, I had presumed this required a join, but seeing as I'm trying to select a parent object containing two related types of children, I can't see what would go in the join's "select new" clause.
As far as I am aware Include does not support this type of sub-querying. Your best option is to use projection e.g.
List<DevelopmentType> devTypes =
defaults.Include(x => x.DefaultCharges)
.Include(x => x.OverrideCharges)
.Select(x => new {
DevType = x.DevelopmentType,
Zones = x.OverrideCharges.Where(oc => oc.ChargingSchedule_RowId == cs.RowId)
.Select(oc => oc.Zone).ToList()
})
.Select(x => x.DevType)
.ToList();