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()
Related
Currently I am doing a keyword search on the Plates table (Name column) but also have a Search (searching on SearchTerm column) table which contains Plat Id's that I also want to search and return the corresponding platforms.
The code below works but I'd like to simplify the logic using an .Include statement if possible although I'm not quite sure how. Any help would be greatly appreciated.
if (!string.IsNullOrEmpty(request.Keyword))
{
var searchTermPlateIds = await _db.Search
.Where(x=> x.SearchTerm.ToLower().Contains(request.Keyword.Trim().ToLower()))
.Select(x => x.PlatformId)
.ToListAsync(ct);
var plateFromPlateIds = await _db.Plate
.OrderBy(x => x.Name)
.Where(x => searchTermPlateIds.Contains(x.Id) && x.Status != PlateStatus.Disabled)
.ToListAsync(ct);
plates = await _db.Plates
.OrderBy(x => x.Name)
.Where(x => !string.IsNullOrEmpty(request.Keyword.Trim()) && x.Name.ToLower().Contains(request.Keyword.Trim().ToLower()) && x.Status != PlateStatus.Disabled)
.ToListAsync(ct);
plates = plates.Union(platesFromPlateIds).ToList();
}
Remember simple thing, Include ONLY for loading related data, not for filtering.
What we can do here - optimize query, to make only one request to database, instead of three.
var query = _db.Plates
.Where(x => x.Status != PlateStatus.Disabled);
if (!string.IsNullOrEmpty(request.Keyword))
{
// do not materialize Ids
var searchTermPlateIds = _db.Search
.Where(x => x.SearchTerm.ToLower().Contains(request.Keyword.Trim().ToLower()))
.Select(x => x.PlatformId);
// queryable will be combined into one query
query = query
.Where(x => searchTermPlateIds.Contains(x.Id);
}
// final materialization, here you can add Includes if needed.
var plates = await query
.OrderBy(x => x.Name)
.ToListAsync(ct);
I am currently working with a schema which contains entities, each containing attributes and each of them contain one type specification. I want extract a list which fulfills a specific attribute requirement it being of the type lookup and a specific specification.
I have tried this so far
schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.SelectMany(x => x.Entities
.Where(x => x.Attributes
.Any(y => y.Type == DataType.Lookup &&
y.TypeSpecification.EntityInternalName == "Weather")));
This returns a list of entities which fulfills the requirement, but none of the entities has any attributes?
How do I also include this within the query?
I tried adding include at the end,
var entitiesWithLookupsTolookupEntityName = schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.Include(x=> x.Entities)
.ThenInclude(x=>x.Attributes)
.ThenInclude(x=>x.TypeSpecification)
.SelectMany(x => x.Entities
.Where(x => x.Attributes
.Any(y => y.Type == DataType.Lookup &&
y.TypeSpecification.EntityInternalName == "Weather")));
but this just include all attributes and etc. ignoring the Any condition for the attributes..
How to get around this?
I tried splitting it a bit
var entitiesWithLookupsTolookupEntityName = schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.Take(1)
.SelectMany(x => x.Entities
.Where(y => y.Attributes
.Any(z => z.Type == DataType.Lookup && z.AttributeTypeSpecification.EntityInternalName == lookupEntityName))).AsEnumerable();
IList<ReferenceView> output = new List<ReferenceView>();
foreach (var entity in entitiesWithLookupsTolookupEntityName)
{
var attribute = schemaRepository.Many().OrderByDescending(x => x.Version).First().Entities
.Where(x => x.InternalName == entity.InternalName);
}
but here I get an exception every in for loop
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);
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();
I'm trying to understand how this may work.
What I would have is to have all Trees from a given list of IDs that has not rotten apples.
Looks easy but I'm fresh of NHibernate, not so good in SQL and as you can see I'm stuck.
I wrote this code down here:
Tree treeitem = null;
QueryOver<Apple> qapple = QueryOver.Of<Apple>()
.Where(x => (!x.IsRotten))
.And(Restrictions.IdEq(Projections.Property<Tree>(y => y.Id)))
// Or this one...
//.And(Restrictions.EqProperty(
// Projections.Property<Apple>(y => y.Tree.Id),
// Projections.Property<Tree>(y => y.Id)))
.Select(x => x.Id);
return this.NHibernateSession.QueryOver<Tree>()
.Where(x => x.Id.IsIn(ListOfTreeId))
.WithSubquery.WhereExists<Apple>(qapple)
.SelectList(list => list
.Select(z => z.Id).WithAlias(() => treeitem.Id)
.Select(z => z.Name).WithAlias(() => treeitem.Name)
.Select(z => z.Type).WithAlias(() => critem.Type)
.TransformUsing(Transformers.AliasToBean<Tree>())
.List<T>();
And the pseudo SQL I get is something like this:
SELECT id, name, type FROM trees WHERE id IN (1, 2, 3)
AND EXIST(SELECT id FROM apples WHERE NOT rotten AND apples.idtree = apples.id)
As you can see there's a problem with the subquery that use the same table Id instead of something like that:
EXIST(SELECT id FROM apples WHERE NOT rotten AND apples.idtree = tree.id)
I'm bit lost actually. Maybe there's another way to build this up.
Any help is welcome, thanks.
Im not sure why you are using resulttransformer when the return type is the same as the query type
return NHibernateSession.QueryOver<Tree>()
.Where(t => t.Id.IsIn(ListOfTreeId))
.JoinQueryOver<Apple>(t => t.Apples)
.Where(a => !a.IsRotten)
.List();
Update: the Compiler chooses ICollection<Apple> while it really should choose Apple therefor specify the generic argument in JoinQueryOver explicitly
Update2: to get them unique
opt 1)
...
.SetResultTransformer(Transformers.DistinctRootEntity());
.List();
opt 2)
Tree treeAlias = null;
var nonRottenApples = QueryOver.Of<Apple>()
.Where(a => !a.IsRotten)
.Where(a => a.Tree.Id == treeAlias.Id)
.Select(x => x.Id); <- optional
return NHibernateSession.QueryOver(() => treeAlias)
.Where(t => t.Id.IsIn(ListOfTreeId))
.WithSubquery.WhereExists(nonRottenApples)
.List();