I am wondering if there is an easier way of adding together summed columns inside a group by query using Linq. I am wanting to check wether column1 + column2 is greater than 2250, and if so do something...
Below is a snippet of code im using, a much slimmed down version for use here
from contact in _db.Worksheets
join person in _db.MyTable on contact.Email equals
person.EmailAddress
orderby contact.ShiftDate ascending
select new
{
EmployeeNumber = person.EmployeeNumber,
Overtime1= contact.Overtime1,
Overtime2= contact.Overtime2,
ShiftDate = contact.ShiftDate,
} into t1
group t1 by t1.EmployeeNumber into pg
select (new
{
OvertimeTotal = pg.Sum(x => x.ShiftDate >= vStart1 && x.ShiftDate <= vEnd1 ? x.Overtime1 : 0)
+ pg.Sum(x => x.ShiftDate >= vStart1 && x.ShiftDate <= vEnd1 ? x.Overtime2 : 0) > 2250 (....then do something)
I was wondering if you could do something like the below. (Which I have tried and it doesnt work)
I am using entity framework too, so realise there may be complications converting this type of query
OvertimeTotal = pg.Sum(x => x.ShiftDate >= vStart1 && x.ShiftDate <= vEnd1 ? x.Overtime1 + x.Overtime2 : 0)
It would help if you specify what you want when there is overtime. The following will return the employee numbers with overtime.
The approach is a little different from yours, as it filters the worksheet records before joining with the person records for optimization reasons. It then filters to just the employees that have overtime.
I'm assuming an employee has overtime if overtime1 + overtime2 of all their shifts in the specified date range adds up to more than 2250.
var employeesWithOvertime = _db.Worksheets
.Where(w => w.ShiftDate >= vStart1 && w.ShiftDate <= vEnd1)
.Join(_db.MyTable, w => w.Email, p => p.EmailAddress, (w, p) => new
{
EmployeeNumber = p.EmployeeNumber,
Overtime1 = w.Overtime1,
Overtime2 = w.Overtime2
})
.GroupBy(x => x.EmployeeNumber)
.Select(g => new
{
EmployeeNumber = g.Key,
OvertimeTotal = g.Sum(x => x.Overtime1 + x.Overtime2)
})
.Where(x => x.OvertimeTotal > 2250);
Related
I've got two tables: Index and Codes
when one condition is true, I need to check whether the Index is still valid and for that I need to get EndDate of the code which is in Codes table (as I've got only id of code in Index table)
This is how I do that:
1) First I get all Codes that have ended already (approx 3k+ items)
var goods = _context.Codes.Select(a =>
new Codes
{
Loid = a.Loid,
Pid = a.Pid,
Code = a.Code,
Startdate = a.Startdate,
Enddate = a.Enddate
})
.Where(x => x.Enddate < DateTime.Now)
.ToList();
then I'm taking my Index and adding values there as well as CodeEnd from Codes list above:
query = _context.VpAbcIndex
.AsNoTracking()
.Select(e => new VpAbcIndex
{
Id = e.Id,
ParentName = e.ParentName ?? "!",
ParentEndDate = e.ParentEndDate,
ParentStartDate = e.ParentStartDate,
ParentCode = e.ParentCode,
ParentNote = e.ParentNote ?? "",
ParentStatus = e.ParentStatus,
ChildName = e.ChildName ?? "!",
ChildId = e.ChildId,
ChildEndDate = e.ChildEndDate,
ChildStartDate = e.ChildStartDate,
CodeEnd = (filter.Level == 0) ? goods
.FirstOrDefault( x => x.Code == e.ParentCode).Enddate :
(filter.Level == 1) ? goods
.FirstOrDefault(x => x.Code == e.ChildCode).Enddate :
(filter.Level == 2) ?
goods
.FirstOrDefault(x => x.Code == e.GrandChildCode).Enddate : null
})
;
}
That's approx 10k items. It doesn't seem that much however it takes quite a long time for them to appear in my browser. Am I doing something wrong? Is there a faster way to join these values?
var testingAll = (from ac in metaData.AcTable
where ac.Call >= DateTime.Now.AddMonths(-2) && ac.Call <= DateTime.Now
group adminCall by ac.LanguageCode into acc
select new { lang = acc.Key, count = acc.Count() }).ToDictionary(x => x.lang, y => y.count).OrderByDescending(x => x.Key);
Can I have filter again after the datetime ?
Something like this:
var Today = testingAll.Where( /*x => x.Call >= DateTime.Now.AddDays(-2)*/)
I think you want something like
var testingAll = (from ac in metaData.AcTable
where ac.Call >= DateTime.Now.AddMonths(-2) && ac.Call <= DateTime.Now
group adminCall by adminCall.LanguageCode into ac
select ac
this should give back a collection where you can then query a number of times.
The short answer is that you can't do this. Think of it this way, the problem is effectively the same as me giving you the average age of children in a class and then you taking that number and trying to work out the average age of the boys only - it's not possible without the source data.
Now you could maybe do this by building an expression and spending a lot of effort there, but it would still have to re-query the database.
If you really want to abstract it away slightly, then you could create a function that takes the where predicate as a parameter:
public IEnumerable<KeyValuePair<string, int>> GetFilteredCalls(
Expression<Func<Call, bool>> predicate)
{
return calls
.Where(predicate)
.GroupBy(c => c.LanguageCode)
.Select(g => new { Lang = g.Key, Count = g.Count() })
.ToDictionary(x => x.Lang, y => y.Count)
.OrderByDescending(x => x.Key);
}
And you use it like this:
var calls = GetFilteredCalls(c => c.Call >= DateTime.Now.AddMonths(-2)
&& c.Call <= DateTime.Now);
var moreCalls = GetFilteredCalls(c => c.Call >= DateTime.Now.AddDays(-2)
&& c.Call <= DateTime.Now);
This is my Code where I am fetching data.
var list = (from u in _dbContext.Users
where u.IsActive
&& u.IsVisible
&& u.IsPuller.HasValue
&& u.IsPuller.Value
select new PartsPullerUsers
{
AvatarCroppedAbsolutePath = u.AvatarCroppedAbsolutePath,
Bio = u.Bio,
CreateDateTime = u.CreationDate,
Id = u.Id,
ModifieDateTime = u.LastModificationDate,
ReviewCount = u.ReviewsReceived.Count(review => review.IsActive && review.IsVisible),
UserName = u.UserName,
Locations = (from ul in _dbContext.UserLocationRelationships
join l in _dbContext.Locations on ul.LocationId equals l.Id
where ul.IsActive && ul.UserId == u.Id
select new PartsPullerLocation
{
LocationId = ul.LocationId,
Name = ul.Location.Name
}),
Rating = u.GetPullerRating()
});
Now Here is my Extension.
public static int GetPullerRating(this User source)
{
var reviewCount = source.ReviewsReceived.Count(r => r.IsActive && r.IsVisible);
if (reviewCount == 0)
return 0;
var totalSum = source.ReviewsReceived.Where(r => r.IsActive && r.IsVisible).Sum(r => r.Rating);
var averageRating = totalSum / reviewCount;
return averageRating;
}
I have check this Post LINQ to Entities does not recognize the method
And I come to know I need to use
public System.Linq.Expressions.Expression<Func<Row52.Data.Entities.User, int>> GetPullerRatingtest
But how ?
Thanks
You can use conditionals inside LINQ to Entity queries:
AverageRating = u.ReviewsReceived.Count(r => r.IsActive && r.IsVisible) > 0 ?
u.ReviewsReceived.Where(r => r.IsActive && r.IsVisible).Sum(r => r.Rating) /
u.ReviewsReceived.Count(r => r.IsActive && r.IsVisible)
: 0
This will be calculated by the server, and returned as part of your list. Although with 10 million rows like you said, I would do some serious filtering before executing this.
Code within LINQ (to Entities) query is executed within database, so you can't put random C# code there. So you should either use user.GetPullerRating() after it is retrieved or create a property if you don't want to do the calculation every time.
You can also do:
foreach (var u in list)
u.Rating = u.GetPullerRating()
By the way, why is it extension method.
Consider the following LINQ statement:
var posts = db.Posts
.Where(p => p.Votes.Count > 0 && p.User.Confirmed)
.Select(p => new
{
PostId = p.PostId,
Votes = p.Votes.Count(),
Hours = EntityFunctions.DiffHours(DateTime.UtcNow, p.Timestamp)
})
.Select(p1 => new
{
PostId = p1.PostId,
Votes = p1.Votes,
Group = p1.Hours <= 24 ? 24 :
p1.Hours <= 168 ? 168 :
p1.Hours <= 720 ? 720 : 0
})
.Where(p2 => p2.Group != 0);
It successfully groups a listing of posts into their respective groups: 24 hours, 168 hours, and 720 hours.
However, now I need to get the PostId that has the Max Votes for each group. How do I do that?
var postIds = posts.OrderByDescending(x => x.PostId).GroupBy(x => x.Group)
.Select(x => x.First().PostId);
Or, for a bit more clarity (IMHO), and (I think) less speed:
var postIds = posts.GroupBy(x => x.Group).Select(g => g.Max(p => p.PostId));
The former has the benefit that if you want the post, and not just the PostId, you have that available more easily.
I was looking at this but kind of slow. It's a little different syntax so I'll post it anyway
var groups = (from p in posts
group p by p.Group into g
select new
{
Id = g.Max(p => p.Id),
Group = g.Key
}).ToList();
var bestPosts = (from p in posts
join j in groups on new {p.Group, p.Votes} equals new {j.Group, j.Votes}
select p).ToList();
Groups according to "GroupByField" and selects the max.
var query = from o in _context.Objects
group o by o.GroupByField
into group
select new
{
maxParameter = (from o in group orderby o.OrderByField select o).Last()
};
and then in order to select the original (max) objects
var largest = query.Select(q => q.maxParameter).ToList();
I am using EF with lambda expression to query a table.
var eventToPushCollage = eventsForEvaluation.Where(x => x.DateTimeStart > currentDateTime && currentDateTime >= x.DateTimeStart.AddMinutes(-15));
Table eventsForEvaluation has a property Id.
In my DB I have also another table called PushedEvents with an Id property.
Note: the two tables have no a Foreign Key relationship.
I need to apply another filtering to my query, and fetch all the records that are also NOT present in the second table PushedEvents.
I would like to know if it is possible and a sample code.
Use group join:
var eventToPushCollage =
from e in eventsForEvaluation
join p in PushedEvents on e.Id equals p.Id into g
where e.DateTimeStart > currentDateTime &&
currentDateTime >= e.DateTimeStart.AddMinutes(-15) &&
g.Count() == 0 // NOT present in second table
select e;
UPDATE (method syntax)
var eventToPushCollage = eventsForEvaluation
.GroupJoin(PushedEvents,
e => e.Id,
p => p.Id,
(e,g) => new { e, g })
.Where(x => x.e.DateTimeStart > currentDateTime &&
currentDateTime >= x.e.DateTimeStart.AddMinutes(-15) &&
x.g.Count() == 0)
.Select(x => x.e);
Try this
var eventToPushCollage =
from x in eventsForEvaluation
where
!PushedEvents.Any(item => item.Id == x.Id)
&& x.DateTimeStart > currentDateTime
&& currentDateTime >= x.DateTimeStart.AddMinutes(-15)
select x;