LINQ distinct and sorting - c#

I have the following query, which I wish to be distinct on "RegistrationNumber", and sorted by "DriverToLoad".
Distinct/grouping works fine. However, my query sorts by "RegistrationNumber" apparently ignoring the "OrderBy":
Drive = await _db.Drive
.Where(m => m.StatusId == 5 || m.StatusId == 1010 || m.StatusId == 1012)
.Include(s => s.DriveStatus)
.Include(d => d.Location)
.Include(f => f.Item)
.GroupBy(m => m.RegistrationNumber)
.Select(g => g.OrderBy(m => m.DriverToLoad).First())
.ToListAsync(),
Any LINQ experts who can point out what the problem is, and how to solve it?
I'm working with .NET7.
Thanks a lot in advance.

I'm going to suggest you want something like this:
var Drive =
await _db.Drive
.Where(m => m.StatusId == 5 || m.StatusId == 1010 || m.StatusId == 1012)
.Include(s => s.DriveStatus)
.Include(d => d.Location)
.Include(f => f.Item)
.GroupBy(m => m.RegistrationNumber, m => m.DriverToLoad)
.SelectMany(g => g.OrderBy(x => x).Take(1))
.ToListAsync();
Or like this:
var Drive =
await
(
from m in _db.Drive
.Include(s => s.DriveStatus)
.Include(d => d.Location)
.Include(f => f.Item)
where m.StatusId == 5 || m.StatusId == 1010 || m.StatusId == 1012
group m.DriverToLoad by m.RegistrationNumber into g
from d in g.OrderBy(x => x).Take(1)
select d
)
.ToListAsync();

I have solved it, but probably not the nicest way (...more like a "hack" I would say).
var drive = await _db.Drive
.Where(m => m.StatusId == 5 || m.StatusId == 1010 || m.StatusId == 1012)
.Include(s => s.DriveStatus)
.Include(d => d.Location)
.Include(f => f.Item)
.GroupBy(m => m.RegistrationNumber)
.Select(g => g.OrderBy(m => m.DriverToLoad).First())
.ToListAsync();
DriveListViewModel model = new DriveListViewModel()
{
Drive = drive.OrderBy(a => a.DriverToLoad),
};
This way I get the sorting I wish, and can return that to the view.
Haven't yet seen the power of LINQ, so maybe I should aim for using ADO and SQL directly in my controllers, and somehow convert that to lists to be returned to my views ;-)
Thank you all for all your great suggestions. I really appreciate it.

Related

Looking to combine two methods with c#

HI all i have below two methods and looking to combine both and one method does not need any joins and other method need those how can i combine these two methods
internal static IQueryable<ConstructionSet> CalculateSelectedConstructionSetsForOSM(Guid? ashraeClimateZoneId,
Guid? energyCodeId,
Guid? massingTypeId,
APIDbContext dbContext)
{
if (dbContext is null)
{
throw new ArgumentNullException(nameof(dbContext));
}
return dbContext.ConstructionSets.Include(p => p.AshraeClimateZone)
.Include(p => p.MassingType)
.Include(p => p.SourceOfData)
.Include(p => p.ExteriorWall)
.Include(p => p.ExteriorFloor)
.Include(p => p.InteriorFloor)
.Include(p => p.InteriorWall)
.Include(p => p.SlabOnGrade)
.Include(p => p.BelowGradeWall)
.Include(p => p.Glazing)
.Include(p => p.Roof)
.Where(p => p.AshraeClimateZoneId == ashraeClimateZoneId
&& p.SourceOfDataId == energyCodeId
&& p.MassingTypeId == massingTypeId
&& p.Revision != null
&& p.IsApproved == true)
.AsEnumerable()
.OrderByDescending(i => i.Revision)
.GroupBy(i => i.InitialRevisionId)
.Select(g => g.First()).AsQueryable();
}
the below one is another method
internal static IQueryable<ConstructionSet> CalculateSelectedConstructionSetsForProject(Guid? ashraeClimateZoneId,
Guid? energyCodeId,
Guid? massingTypeId,
APIDbContext dbContext)
{
if (dbContext is null)
{
throw new ArgumentNullException(nameof(dbContext));
}
return dbContext.ConstructionSets.Include(p => p.AshraeClimateZone)
.Include(p => p.MassingType)
.Include(p => p.SourceOfData)
.Where(p => p.AshraeClimateZoneId == ashraeClimateZoneId
&& p.SourceOfDataId == energyCodeId
&& p.MassingTypeId == massingTypeId
&& p.Revision != null
&& p.IsApproved == true)
.AsEnumerable()
.OrderByDescending(i => i.Revision)
.GroupBy(i => i.InitialRevisionId)
.Select(g => g.First()).AsQueryable();
}
Could any one please let me know how can i combine these methods, many thanks in advance.
Assuming by "combine them" you mean "refactor them so a single method can handle both cases", you can do something like this:
internal static IQueryable<ConstructionSet> CalculateSelectedConstructionSets(Guid? ashraeClimateZoneId,
Guid? energyCodeId,
Guid? massingTypeId,
APIDbContext dbContext,
bool includeOsmNavigationProperties)
{
if (dbContext is null)
{
throw new ArgumentNullException(nameof(dbContext));
}
var sets = dbContext.ConstructionSets.Include(p => p.AshraeClimateZone)
.Include(p => p.MassingType)
.Include(p => p.SourceOfData);
if(includeOsmNavigationProperties)
{
sets = sets.Include(p => p.ExteriorWall)
.Include(p => p.ExteriorFloor)
.Include(p => p.InteriorFloor)
.Include(p => p.InteriorWall)
.Include(p => p.SlabOnGrade)
.Include(p => p.BelowGradeWall)
.Include(p => p.Glazing)
.Include(p => p.Roof);
}
return sets.Where(p => p.AshraeClimateZoneId == ashraeClimateZoneId
&& p.SourceOfDataId == energyCodeId
&& p.MassingTypeId == massingTypeId
&& p.Revision != null
&& p.IsApproved == true)
.AsEnumerable()
.OrderByDescending(i => i.Revision)
.GroupBy(i => i.InitialRevisionId)
.Select(g => g.First()).AsQueryable();
}
As a side note, you should avoid the pattern of converting this to .AsEnumerable() and then back to .AsQueryable(). Making the result an IQueryable makes subsequent operations on the returned collection much slower (expressions need to be compiled at run-time), and it hides the fact that you won't be performing subsequent operations at the database layer. Someone might add a Where() clause and not realize that they're still causing the entire set of data to get loaded out of the database.

LINQ OrderBy for complex entity

I have complex query:
var containers = this.Repository.Containers
.Include(x => x.PostsContainers)
.ThenInclude(x => x.Post)
.ThenInclude(x => x.TasksPosts)
.ThenInclude(x => x.Task)
.ThenInclude(x => x.AssignedToUser);
var items = containers.Where(x => x.PostsContainers
.Any(y => y.Post.TasksPosts
.Any(z => z.Task.DateDue <= DateTime.UtcNow.AddDays(7)
&& !z.Task.Completed
&& z.Task.AssignedToUserId.Value == userId)));
I got items but I also need to sort these items by Task.DueDate and extract AssignedToUser name.
What's the best way to do it (with good performance, without code duplication)? Maybe I need to rewrite my code?
Try to refactor it this way:
var dateDue = DateTime.UtcNow.AddDays(7)
var result = (from c in this.Repository.Containers
from pc in c.PostsContainers
from tp in pc.Post.TasksPosts
where tp.Task.DateDue <= dateDue
&& !tp.Task.Completed
&& tp.Task.AssignedToUserId == userId
orderby tp.Task.DueDate
select new
{
Container = c,
tp.Task.AssignedToUser
})
.ToList();

NHibernate and MySql Missing column Exception

From time to time my web application begins throwing the following errors.
Using NHibernate 4.0.0.4000 and MySql.Data 6.8.3
Stack Trace
ERROR [(null)] - Message:could not execute query
NHibernate log
NHibernate.Util.ADOExceptionReporter WARN - System.IndexOutOfRangeException: Could not find specified column in results:
Once once of these errors occur it begins to happen frequently until the web application is restart.
It's odd that it only happens to some users and not all. Also I noticed in this particular log message the values of p4 and p5 should be swapped.
Is this an issue with the query cache?
Does anyone have some insight into why this is happening?
If it helps here is the gnarly query (but I see this error on much simpler queries as well)
FunderInfoViewModel funderDto = null;
Funder funderAlias = null;
Contact contactAlias = null;
var totalOpportunitiesAwardedCount = QueryOver.Of<Opportunity>()
.Where(o => o.Funder.Id == funderAlias.Id)
.And(o => o.Status == OpportunityStatus.Awarded || o.Status == OpportunityStatus.AwardedClosed)
.SelectList(list => list
.SelectCount(o => o.Id));
var totalOpportunitiesAwardedSum = QueryOver.Of<Opportunity>()
.Where(o => o.Funder.Id == funderAlias.Id)
.And(o => o.Status == OpportunityStatus.Awarded || o.Status == OpportunityStatus.AwardedClosed)
.SelectList(list => list
.SelectSum(o => o.AmountAwarded));
var totalOpportunitiesCount = QueryOver.Of<Opportunity>()
.Where(o => o.Funder.Id == funderAlias.Id)
.SelectList(list => list
.SelectCount(o => o.Id));
IEnumerable<FunderInfoViewModel> funders = _session.QueryOver(() => funderAlias)
.Left.JoinAlias(f => f.Contacts, () => contactAlias, x => x.IsDefault)
.Where(o => o.Organization.Id == organizationId)
.SelectList(list => list
.Select(x => x.Id)
.WithAlias(() => funderDto.Id)
.Select(x => x.Name)
.WithAlias(() => funderDto.Name)
.Select(x => x.Description)
.WithAlias(() => funderDto.Description)
.Select(x => x.AreasOfInterest)
.WithAlias(() => funderDto.AreasOfInterest)
.Select(x => x.Type)
.WithAlias(() => funderDto.FunderType)
.Select(x => x.TaxId)
.WithAlias(() => funderDto.TaxId)
.Select(x => x.PhoneNumber)
.WithAlias(() => funderDto.PhoneNumber)
.Select(x => x.FaxNumber)
.WithAlias(() => funderDto.FaxNumber)
.Select(x => x.EmailAddress)
.WithAlias(() => funderDto.EmailAddress)
.Select(x => x.Website)
.WithAlias(() => funderDto.Website)
.Select(x => x.CustomLink)
.WithAlias(() => funderDto.CustomLink)
.Select(x => x.MinimumFundingRange)
.WithAlias(() => funderDto.MinimumFundingRange)
.Select(x => x.MaximumFundingRange)
.WithAlias(() => funderDto.MaximumFundingRange)
.Select(() => contactAlias.FirstName)
.WithAlias(() => funderDto.PrimaryContactFirstName)
.Select(() => contactAlias.LastName)
.WithAlias(() => funderDto.PrimaryContactLastName)
.Select(() => contactAlias.Title)
.WithAlias(() => funderDto.PrimaryContactTitle)
.SelectSubQuery(totalOpportunitiesAwardedCount)
.WithAlias(() => funderDto.AwardedOpportunitiesCount)
.SelectSubQuery(totalOpportunitiesAwardedSum)
.WithAlias(() => funderDto.AwardedOpportunitiesValue)
.SelectSubQuery(totalOpportunitiesCount)
.WithAlias(() => funderDto.OpportunitiesCount)
)
.OrderBy(f => f.Name)
.Asc
.TransformUsing(Transformers.AliasToBean<FunderInfoViewModel>())
.List<FunderInfoViewModel>();
Ok got the problem. Its because some of the arguement you are passing in your prepared statement is null, thats why this error. I had similar issue earlier and i solved it by checking only if it is not null add it in query filtering.
Also there is another possibility of row lock by any other query. Are you using lock in mysql query?
2nd problem seems like see the solution stated below
https://forums.asp.net/t/1230295.aspx?IDataReader+Could+not+find+specified+column+in+results+

LINQ OrderBy/ThenBy ChildrenCollection.SortOrder

EDIT:
Both queries in this example are not ordering by the location sort order. Does anyone have any suggestions on how to do this?
I need some help with ordering an entity collection of groups by a child entity column. I have a one to many relationship from groups to locations.
I need to get the groups ordered by the group name and then by the location sort order.
Since the child entity is a collection I am having trouble.
Here is code I am using that works but returns duplicates:
var groups = db.GetNavigationGroups()
.Where(g => selectedLocation > 0 ? g.NavigationGroupLocations.Any(l => l.Location == selectedLocation) : true)
.SelectMany(g => g.NavigationGroupLocations, (g, l) => new { g, l })
.OrderBy(x => x.g.Name)
.ThenBy(x => x.l.SortOrder)
.Select(x => x.g);
Then I tried this approach using FirstOrDefault():
List<NavigationGroup> groups = db.DataModel.NavigationGroups
.Where(g => selectedLocation > 0 ? g.NavigationGroupLocations.Any(l => l.Location == selectedLocation) : true)
.OrderBy(g => g.Name)
.ThenBy(g => g.NavigationGroupLocations.Where(l => l.Location == selectedLocation && l.GroupID == g.ID).OrderBy(l => l.SortOrder).FirstOrDefault())
.ToList();
The problem is that I cannot seem to get the groups in the order I need them to be in based on the locations SortOrder column.
The second query you have looks like you're trying to sort by the NavigationGroupLocations object instead of on it's fields. Have you tried:
List<NavigationGroup> groups = db.DataModel.NavigationGroups
.Where(g => selectedLocation > 0 ? g.NavigationGroupLocations.Any(l => l.Location == selectedLocation) : true)
.OrderBy(g => g.Name)
.ThenBy(g => g.NavigationGroupLocations.Where(l => l.Location == selectedLocation && l.GroupID == g.ID).OrderBy(l => l.SortOrder).FirstOrDefault().SortOrder)
.ToList();
You might also want to add your selectedLocation condition to the order clause.
List<NavigationGroup> groups = db.DataModel.NavigationGroups
.Where(g => selectedLocation > 0 ? g.NavigationGroupLocations.Any(l => l.Location == selectedLocation) : true)
.OrderBy(g => g.Name)
.ThenBy(g => g.NavigationGroupLocations.Where(l => selectedLocation > 0 ? l.Location == selectedLocation : true && l.GroupID == g.ID).OrderBy(l => l.SortOrder).FirstOrDefault().SortOrder)
.ToList();

Nhibernate QueryOver Left Outer Joins with conditions

I found a few resources online but havent really been able to sort this one out
Basically I have a query which has two left outter joins on it
var query = session.QueryOver<NewsPost>(() => newsPostAlias)
.Left.JoinQueryOver(x => newsPostAlias.PostedBy, () => userAlias)
.Left.JoinQueryOver(x => newsPostAlias.Category, () => categoryAlias)
.Fetch(x => x.PostedBy).Eager
.Fetch(x => x.Category).Eager
.Where(x => !x.Deleted);
This might be an invalid way of doing it but it appears to not break. Now what I want to do is on the two tables which have left outter joins on i want to make sure the Deleted column in both these tables is false.
However whenever I add that restriction the results only return when the foreign key column in news post is populated, but since this is nullable and why i made it a left outter join this isnt desirable.
Whats the best way of basically making it
.Where(x => !x.Deleted && !x.PostedBy.Deleted && !x.Category.Deleted);
I've looked into multiqueries, futures and disjunctions, I'm not sure what approach should be taken, obviously I can think of a few ways (bad ways my gut tells me) of doing this but whats the right way? :)
Thanks
EDIT - Accepted Answer Modification
return session.QueryOver(() => newsPostAlias)
.Fetch(x => x.PostedBy).Eager
.Fetch(x => x.Category).Eager
.Left.JoinQueryOver(() => newsPostAlias.PostedBy, () => postedByAlias)
.Left.JoinQueryOver(() => newsPostAlias.Category, () => categoryAlias)
.Where(() => !newsPostAlias.Deleted)
.And(() => newsPostAlias.PostedBy == null || !postedByAlias.Deleted)
.And(() => newsPostAlias.Category == null || !categoryAlias.Deleted)
.OrderBy(() => newsPostAlias.PostedDate).Desc
.Take(10)
.List();
I suppose your query should look like this
Session.QueryOver<NewsPost>()
.Left.JoinAlias(x => x.PostedBy, () => userAlias)
.Left.JoinAlias(x => x.Category, () => categoryAlias)
.Where(x => !x.Deleted)
.And(x => !userAlias.Deleted)
.And(x => !categoryAlias.Deleted);
This seems to work ...
var posts = session.QueryOver<NewsPost>()
.Left.JoinAlias(x => x.Category, () => category)
.Left.JoinAlias(x => x.PostedBy, () => user)
.Where(x => x.Deleted == false)
.Where(Restrictions
.Or(
Restrictions.Where(() => user.Deleted == false),
Restrictions.Where<NewsPost>(x => x.PostedBy == null)
)
)
.Where(Restrictions
.Or(
Restrictions.Where(() => category.Deleted == false),
Restrictions.Where<NewsPost>(x => x.Category == null)
)
)
.List();
Was this one of the ways you felt would be bad?? If so, could you please explain why? I do not know enough about optimizing sql, hence am asking ...

Categories