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

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));

Related

Querying a deep child object with a certain property, but returning the root object which cascades down

Sorry about the cryptic heading, I have always struggled with this problem.
Say we have a model structure like so:
Schools, which have classes, which in turn have students. I'd like to return a list of schools (as the top-level object), including classes and students where the students are male.
The easy way to do this would be:
var maleStudents = Context.Students
.Include(s => s.Classes)
.ThenInclude(c => c.School)
.Where(s => s.Gender == Gender.Male)
Problem is, this returns a bunch of students with their own duplicate copies of the classes and schools. It also makes it hard to visually display them via 'School' without a bunch of organizing.
Is there an elegant solution or does it require my usual juggle after pulling it all to the server?
As of EF Core 5.0, filtered includes are supported, so you could write your query as follows:
var schools = Context.Schools
.Include(s => s.Classes)
.ThenInclude(c => c.Students.Where(s => s.Gender == Gender.Male));
If your targeting previous versions of EF Core your existing query is fine, as you could perform a GroupBy in the client to project your structure:
var schoolsWithStudents = maleStudents
.GroupBy(x => x.Classes
.First().School);
Although this assumes that each student only belongs to classes relating to the same school.
Update after comment
If you only want to bring back schools that have Male students, you can add an extra Where after the filtered include:
var schools = Context.Schools
.Include(s => s.Classes)
.ThenInclude(c => c.Students.Where(s => s.Gender == Gender.Male))
.Where(s => s.Classes.Any(c => c.Students.Any(st => st.Gender == Gender.Male)));

NHibernate - where .. in .. and future

I have two models: Thing and ThingStatus. Thing has an Id and some other fields. ThingStatus is a model which stores Status enum corresponding to id of Thing. Now I want to fetch Things that have Status != Completed.
What I try to do now looks like this:
var unfinishedIds = session.QueryOver<ThingStatus>()
.Where(t => t.Status != StatusEnum.Completed)
.Select(t => t.Id)
.List<long>()
.ToArray();
var unfinishedThings = session.QueryOver<Thing>()
.WhereRestriction(t => t.Id)
.IsIn(unfinishedIds)
.List<Thing>();
As far as I understand, in such case unfinishedIds will be fetched from database and only after that used as a filter in unfinishedThings query. Is there any way to avoid that and have the query optimizer select the right way to do that? I've heard there are some futures available with nhibernate but I'm not sure how they'd help here.
You can use a subquery if you can't create a NHibernate relationship between the two entities. No relationship --> no JoinAlias (or JoinQueryOver) possible.
With a subquery:
var unfinishedIds = QueryOver.Of<ThingStatus>()
.Where(t => t.Status != StatusEnum.Completed)
.Select(t => t.Id);
var unfinishedThings = session.QueryOver<Thing>()
.WithSubquery.WhereProperty(t => t.Id).In(unfinishedIds)
.List<Thing>();
(note the use of QueryOver.Of<>)
The query is equivalent to writing:
SELECT * FROM Things WHERE Id IN (SELECT Id FROM ThingsStatuses WHERE Status <> 'Completed')

Linq to select objects where children have no associated children

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());

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();
}

Converting an IEnumerable list to a grouped list in Linq

I have a function that returns the following type:
IEnumerable<IGrouping<String, ExportTransaction>>
In this function I create a list of the following type, using a linq expression:
IEnumerable<ExportTransaction>
The Linq code looks like this:
IEnumerable<ExportTransaction> transactions = ctx.ExportTransactions
.Where(x => x.Id != null);
How can I convert “transactions” to a grouped list of the type shown at the top of this page. The function does various things with the “transactions” so It must stay as “IEnumerable” inside the function but must be converted to the grouped list when returned.
I'll assume that the Transactions have a name property and that's what you want to group the Transactions by. If that's not the case, all you have to do is change the property in the GroupBy call:
var transactions = ctx.ExportTranactions
.Where(x => x.Id != null)
.GroupBy(x => x.Name);
Does it really need to be grouped and if so what do you want to group it by? It sounds to me like you have your data set and you could be trying to force it into a format you don't necessarily need.
Typically to group a data set you'd do something like:
var grouping = ctx.ExportTransactions.Where(x => x.Id != null)
.GroupBy(x => x.Description);
That would create an IEnumerable<IGrouping<string, ExportTransaction>> grouping all the transactions with an identical description, presuming that each transaction had description.
If you need all the records in a single group you can always do the following:
var grouping = ctx.ExportTransactions.Where(x => x.Id != null)
.GroupBy(x => string.Empty);
Which will give you what you need with the group key being an empty string, but I'd strongly advise against it and instead suggest looking at why you need to return a grouping when you don't seem to want to group by anything.

Categories

Resources