Linq to select objects where children have no associated children - c#

Have the following hierarchy:
AvailabilityCounts can have many MemberCounts
Each MemberCount can have one Booking
I need a linq statement that will select all AvailabilityCounts which have at least one child MemberCount for which there is no associated Booking.
I've got this far:
var test = rep.AvailabilityCounts_Get()
.Where(a => a.MemberCounts
.Where(m => m.Bookings.Any())
.Count() > 0);
This works when the AvailabilityCount has multiple MemberCounts, but ends up with opposite results when there's only one MemberCount.
Apologies for using other people's brains to do my work, but struggling to see the logic in this.

Try this one:
var test = rep.AvailabilityCounts_Get().Where(a => a.MemberCounts
.Any(m => !m.Bookings.Any()));

I would say an Any() and a not Any() should do what you want.
rep.AvailabilityCounts_Get.Where(m => m.MemberCounts.Any(x => !x.Bookings.Any());

Related

Why does LINQ SelectMany ignore includes? (and in fact removes loaded data)

var result = ParentTable.Set
.Include("Children")
.Include("Children.GrandChildren")
.SelectMany(p=>p.Children)
.Where(c=>c.GrandChildren.Any(whatever))
will return null, even though
var result = ParentTable.Set
.Include("Children")
.Include("Children.GrandChildren")
has the fully populated data in it.
Its needed to do
var result = ParentTable.Set
.SelectMany(p=>p.Children)
.Include("GrandChildren")
.Where(c=>c.GrandChildren.Any(whatever))
This example is a bit contrived, but I ran into this problem as part of a larger more complicated query where a subselect projection was using SelectMany to populate a count into a particular property, and other data was being projected into other properties from outside the selectMany, and those properties were populating, but the selectmany count was not. It was very confusing how different parts of the query had different parts of the same data lazy loaded!
You should be using ThenInclude to get the children.
I would rewrite your query as:
var result = ParentTable.Set
.Include(p => p.Children)
.ThenInclude(c => c.GrandChildren.Where(gc => gc.whatver))
.SelectMany(p => p.Children)

Issue with many-to-many query with linq to entities

I've got a table
Application
ApplicationID,
NAme
ApplicationSteps
AplicationStepID,
AplicationID,
StepID
ApplicationStepCriterias
ApplicationStepID,
CriteriaID
So I've got one SelectedCriteriaID - a user choose from a dropdown one criteria and he wants all the applications which has this SelectedCriteriaID in the table ApplicationStepCriterias
I tried
var ds = context.Applications
.Where(a => a.ApplicationSteps
.Select(x=>x.ApplicationStepCriterias
.Select(t=>t.CriteriaId))
.Contains(SelectesdCriteria));
But as I have as result IEnumerable<IEnumerable<int>> I cannot use Contains
Just I get a list of all the CriteriaIds for each ApplicationStep(also a sequence). Just I cannot think of way to get in one list all the CriteriIds.
First, let me try to get the names right. This is not a pure many-to-many association, because the junction class is part of the class model. It is what I unofficially call a 1-n-1 association. So you have
Application -< ApplicationSteps >- ApplicationStepCriterias
I'd strongly recommend to use singular names for your classes ...
Application -< ApplicationStep >- ApplicationStepCriterion
... so you can use plural for collection property names without getting confused.
If I'm right so far, you query should be
context.Applications
.Where(a => a.ApplicationSteps
.Any(x => selectedCriteria
.Contains(x.ApplicationStepCriterion.CriteriaId));
(and I'd also prefer CriterionId, probably referring to a Criterion class)
You may try something like this:
var applicationStepIds = context.ApplicationStepCriterias
.Where(i => i.CriteriaID == selectedCriteria)
.Select(i => i.ApplicationStepID)
.Distinct();
var applicationIds = context.ApplicationSteps
.Where(i => applicationStepIds.Contains(i.AplicationStepID))
.Select(i => i.AplicationID)
.Distinct();
var result = context.Applications.Where(i => applicationIds.Contains(i.ApplicationId));

Returning Most Relevant Search Results Using LINQ Where/Take

I have a List<Locations> that will be filtered to yield a set of results relevant to a search term.
At the moment, I tried these 'search results' by filtering with the following:
return locations.Where(o => o.Name.Contains(term)).Take(10).ToList();
Problem
If I were to enter 'Chester' as the search term, I will never see the item "Chester" despite it existing in the locations list. The reason for this is that there are 10 or more other items in the list that contain the String "Chester" in their name (Manchester, Dorchester etc.).
How can I use LINQ to first of all take the results that start with the search term?
What I've Got So Far
var startsWithList = list.Where(o => o.Name.StartsWith(term)).Take(10).ToList();
var containsList = list.Where(o => o.Name.StartsWith(term) && !startsWithList.Contains(o)).Take(10 - startsWithList.Count).ToList();
return startsWithList.AddRange(containsList);
I don't like the above code at all. I feel like this should be achieved in one Where as opposed to performing two Where and Take's and combining the two lists.
just order ascending before Take, putting a lower value for items starting with term.
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.StartsWith(term) ? 0 : 1)
//or OrderByDescending(m => m.Name.StartsWith(term))
.Take(10)
.ToList();
adapted with the improvement of MikeT (exact match before StartsWith), you could just do
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.StartsWith(term)
? (m.Name == term ? 0 : 1)
: 2)
.Take(10)
.ToList();
I have created a new github project that uses expression trees to search for text in any number of properties
It also has a RankedSearch() method which returns the matching items with the number of hits for each record meaning you can do the following:
return locations.RankedSearch(term, l => l.Name)
.OrderByDescending(x => x.Hits)
.Take(10)
.ToList();
If you wanted you could search accross multiple properties
return locations.RankedSearch(term, l => l.Name, l => l.City)
... or for multiple terms,
return locations.RankedSearch(l => l.Name, term, "AnotherTerm" )
... or for both multiple properties and multiple terms
return locations.RankedSearch(new[]{term, "AnotherTerm"},
l => l.Name,
l => l.City)
Checkout this post for more information on the SQL generated and other usages:
http://jnye.co/Posts/27/searchextensions-multiple-property-search-support-with-ranking-in-c-sharp
You can download this as a nuget package to:
https://www.nuget.org/packages/NinjaNye.SearchExtensions/
Raphaƫl's Solution will work but if you were say searching for Warwick you could find that it might not put Warwick the top of the list if Warwickshire is also a possible location,using the scores you can also extend this infinitely with more matching methods, as well as tweaking the score values to refine your search order
return locations.Select(l => New {SearchResult=l,
Score=(L.Name == Term ?
100 :
l.Name.StartsWith(term) ?
10 :
l.Name.Contains(term) ?
1 :
0
)})
.OrderByDescending(r=>r.Score)
.Take(10)
.Select(r => r.SearchResult);
note i would probably do this by making a Match method and do the logic in there rather than in the linq like i did above so it would just be
return locations.OrderByDescending(Match).Take(10);
All Solutions will work but the better score can gain more easier as below
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.IndexOf(term))
.Take(10)
.ToList();
as a result each name that contain term at nearest to start, show first.
What about this?
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Length)
.Take(10)
.ToList();

Remove duplicates of a List, selecting by a property value in C#?

I have a list of objects that I need some duplicates removed from. We consider them duplicates if they have the same Id and prefer the one whose booleanValue is false. Here's what I have so far:
objects.GroupBy(x => x.Id).Select(x => x.Where(y => !y.booleanValue));
I've determined that GroupBy is doing no such grouping, so I don't see if any of the other functions are working. Any ideas on this? Thanks in advance.
You can do this:
var results =
from x in objects
group x by x.Id into g
select g.OrderBy(y => y.booleanValue).First();
For every Id it finds in objects, it will select the first element where booleanValue == false, or the the first one (if none of them have booleanValue == false).
If you prefer fluent syntax:
var results = objects.GroupBy(x => x.Id)
.Select(g => g.OrderBy(y => y.booleanValue).First());
Something like this should work:
var result =
objects.GroupBy(x => x.Id).Select(g =>
g.FirstOrDefault(y => !y.booleanValue) ?? g.First())
This assumes that your objects are of a reference type.
Another possibility might be to use Distinct() with a custom IEqualityComparer<>.
This partially answers the question above, but I justed need a really basic solution:
objects.GroupBy(x => x.Id)
.Select(x => x.First())
.ToArray();
The key to getting the original object from the GroupBy() is the Select() getting the First() and the ToArray() gets you an array of your objects, not a Linq object.

OrderBy with Dynamic Linq and one to many relationship in EF

I'd like to implement a module for filtering and paging. I understand that to suceed I had to use Dynamic Linq or Reflection, so I started trying to make it work .. but since the field that contains the text to be filtered in a one to many relationship EF not like it.
This code work fine .. but is static :
List<Domain.Entities.Action> actions = db.Actions.Include("Menus").Include("ActionDetails")
.Where(x => x.ActionDetails.Any(y => y.Language.Culture == _currentCulture))
.OrderBy(y => y.ActionDetails.Select(z => z.Title).Max()).Skip((pager.Index - 1) * pager.Take).Take(pager.Take)
.ToList();
I want the
.Select(z => z.Title)
Dynamic..
Can someone help me .. I Try a lot of thing .. but no sucess
Ju.
In order to accomplish this you need to pass in a parameter of Funt<Action, TResultType> searchCriteria
Not sure what your method signature is like but this would work if you plan on returning a List<string>
public List<string> PerformSearch(Func<Action, string> selectCriteria)
{
return db.Actions.Include("Menus").Include("ActionDetails")
.Where(x => x.ActionDetails.Any(y => y.Language.Culture == _currentCulture))
.OrderBy(y => y.ActionDetails.Select(**selectCriteria**).Max())
.Skip((pager.Index - 1) * pager.Take).Take(pager.Take)
.ToList();
}

Categories

Resources