LINQ - GroupBy Year/Month - c#

I'm trying to populate graph data with total amount(sum) by a last four months, and visually it would look like this:
I've tried so far to group data by year and by a month, but I'm not sure if it's right approach cuz this doesn't work..
Here is the code:
var testQUERY = await _context.Calculation
.AsNoTracking()
.Where(x => (x.PaymentDate != null && x.PaymentDate > DateTime.UtcNow.AddMonths(-4)))
.GroupBy(x => new { x.PaymentDate.Value.Year, x.PaymentDate.Value.Month}).ToListAsync();
Here's my paymentDate :
And I'm wondering how could I group by month only..
Error I'm facing is next:
Error generated for warning
'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning: The
LINQ expression 'GroupBy(new <>f__AnonymousType0`2(Year =
Convert([p].PaymentDate, DateTime).Year, Month =
Convert([p].PaymentDate, DateTime).Month), [p])' could not be
translated and will be evaluated locally.'. This exception can be
suppressed or logged by passing event ID
'RelationalEventId.QueryClientEvaluationWarning' to the
'ConfigureWarnings' method in 'DbContext.OnConfiguring' or
'AddDbContext'.
P.S If I better think because I'm using
x.PaymentDate != null && x.PaymentDate > DateTime.UtcNow.AddMonths(-4)
I don't need new anonymous type where I included year also.. but obliviusly I'm trying to group by column which does not exist.. ?

Try using this one. See comments for possible fixes.
var testQUERY = await _context.Calculation
.AsNoTracking()
.Where(x => x.PaymentDate != null)
.Select(x => new { PaymentDate = x.PaymentDate.Value, Row=x }) // pre-select non-null payment date
.Where(x => x.PaymentDate > DateTime.UtcNow.AddMonths(-4)) // this should go after the grouping, as it might include from just part of the month
.GroupBy(x => new { x.PaymentDate.Year, x.PaymentDate.Month})
.Select(grp=> new { grp.Key.Year, grp.Key.Month, Count = grp.Count()) // flatten group and calculate aggregates
.ToListAsync();

Related

Is possible to combine 2 IDbContextFactory in one query in C#?

I know how to retrieve this data from DB but not sure how to handle this in C#. I have two methods, first returning Clubs I follow and the second returns the results of the club. I want to create a third one which will return results of all clubs I follow.
ImmutableArray<ClubInActiveSessionForMemberSqlModel> myClubs;
using (var context = _contextFactory.CreateDbContext())
{
myClubs = await context
.ClubMembers
.SetTracking(false)
.Where(ClubMember =>
ClubMember.MemberId == memberId &&
ClubMember.ClubMemberRegistrationStatusTypeId == ClubMemberRegistrationStatusTypes.Registered.Id &&
(ClubMember.Club!.Session!.StartDateUtc > DateTime.UtcNow || ClubMember.Club!.Session.EndDateUtc > DateTime.UtcNow))
.Select(ClubMember => new ClubInActiveSessionForMemberSqlModel(
ClubMember.ClubId,
ClubMember.Club!.Name,
ClubMember.Club.Session!.Id,
ClubMember.Club.Session.Name,
ClubMember.Club.Session.SessionTypeId,
ClubMember.Club.ClubStanding!.ClubMatchWins,
ClubMember.Club.ClubStanding.ClubMatchLosses
))
.ToImmutableArrayAsync();
}
And here is how I get the data about matches:
using (var context = _contextFactory.CreateDbContext())
{
return await context
.Clubs
.SetTracking(false)
.Where(t => t.Id == ClubId)
.Select(t => new AllMatchResultsForClubByIdSqlModel(
ClubId,
t.Name,
t.SessionId,
t.Position1ClubMatches!
.Where(tm => !completedOnly || (tm.ClubMatchResult != null && tm.ClubMatchResult.WinnerClubId.HasValue))
.AsQueryable()
.Select(selectClubMatchResultSqlModelExpression)
.AsEnumerable(),
t.Position2ClubMatches!
.Where(tm => !completedOnly || (tm.ClubMatchResult != null && tm.ClubMatchResult.WinnerClubId.HasValue))
.AsQueryable()
.Select(selectClubMatchResultSqlModelExpression)
.AsEnumerable()))
.FirstOrDefaultAsync();
}
Is possible to combine these 2 in 1 or how to do that?
I saw some similar issues, but more or less that is for SQL syntax like:
Multiple Context joining c#
Yes, it is possible to join two queries. You can use 'Join' method.
I'm not going to provide you a full code but you can examine this snippet to get an idea. Use select method to retrieve the data you want after join.
using (var context = _contextFactory.CreateDbContext())
{
var results = await context
.Where(...)
.Join(context.Clubs,
ClubMember => ClubMember.ClubId,
Club => Club.Id,
(ClubMember, Club) => new { ClubMember, Club })
Learn more at Method-Based Query Syntax Examples: Join Operators

C# db query where conditions are met, orderby date and then get the first result

While evaluating some queries we found some possible optimization. The ideia is shown below but I currently don't know how to solve this.
Current query:
public static List<Object> SampleQuerySales(int store_id)
{
var query = (from clients in db.table1.Where(p => p.store_id == store_id)
from sales in db.table2.Where(q => q.customer_id == clients.customer_id))
select new Object {
...
}).ToList();
return query;
}
This returns all sales made, but its required only the latest sale (OperationDate) from a datetime reference. As obvious this became a bottleneck.
My ideia was to make it similar to query below, which is incorrect (doesn't compile). How can I achieve this dataset?
var query = (from clients in db.table1.Where(p => p.store_id == store_id)
from sales in db.table2.Where(q => q.customer_id == clients.customer_id
&& q.OperationDate <= dateReference)
.OrderByDescending(s => s.OperationDate).FirstOrDefault() //error
select new Object {
...
}).Tolist();
Since you only want one value from table2, use let instead of from:
var query = (from client in db.table1.Where(p => p.store_id == store_id)
let mostRecentSaleAfterDateReference = db.table2
.Where(q => q.customer_id == client.customer_id
&& q.OperationDate <= dateReference)
.OrderByDescending(s => s.OperationDate)
.FirstOrDefault()
select new Object {
...
}).Tolist();

LINQ efficiency

Consider the following LINQ statements:
var model = getModel();
// apptId is passed in, not the order, so get the related order id
var order = (model.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => y.OrderId));
var orderId = 0;
var orderId = order.LastOrDefault();
// see if more than one appt is associated to the order
var apptOrders = (model.getMyData
.Where(x => x.OrderId == orderId)
.Select(y => new { y.OrderId, y.AppointmentsId }));
This code works as expected, but I could not help but think that there is a more efficient way to accomplish the goal ( one call to the db ).
Is there a way to combine the two LINQ statements above into one? For this question please assume I need to use LINQ.
You can use GroupBy method to group all orders by OrderId. After applying LastOrDefault and ToList will give you the same result which you get from above code.
Here is a sample code:
var apptOrders = model.getMyData
.Where(x => x.ApptId == apptId)
.GroupBy(s => s.OrderId)
.LastOrDefault().ToList();
Entity Framework can't translate LastOrDefault, but it can handle Contains with sub-queries, so lookup the OrderId as a query and filter the orders by that:
// apptId is passed in, not the order, so get the related order id
var orderId = model.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => y.OrderId);
// see if more than one appt is associated to the order
var apptOrders = model.getMyData
.Where(a => orderId.Contains(a.OrderId))
.Select(a => a.ApptId);
It seems like this is all you need:
var apptOrders =
model
.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => new { y.OrderId, y.AppointmentsId });

Greater and equal predicate does not work- RavenDB session.Query

I'm using session.Query for getting data from RavenDB
I want to get documents where price field greater or equal to price parameter.
I try-
session.Query<Index.Result, Index>()
.Where(x => x.Id == Id
&& x.Price >= Settings.Price)
.TransformWith<Transformer, Transformer.Result>()
.ToList();
But I get wrong results - only 2 documents instead of 3.
If I add the predicate after the query I get the right result =3 documents
var a = session.Query<Index.Result, Index>()
.Where(x => x.Id == Id)
.TransformWith<Transformer, Transformer.Result>()
.ToList();
var b = a.Where(x => x.Price >= Settings.Price)
.ToList();
Why the session.Query doesn't work correctly?
In Lucene Syntax I get the correct results - 3 documents.
session.Advanced.DocumentQuery<Index.Result, Index>()
.Where(string.Format("Id :{0} Price : [{1} TO *]" , Id, Price))
.SetResultTransformer("Transformer")
.SelectFields<Transformer.Result>()
.ToList();
But I want to use the session.Query syntax.
So how can I use the session.Query syntax linq syntax to get the result according to the greeter or equal predicate?
Thank in advance

Get complex object using IN equivalent in LINQ

I have a list of type customer. I need to insert all values of the list in the database before checking if a customer with the same customer number exists for that particular client.
For that I am firing a query to get me all customers who are there in the database having customer number equal to ones in the list. The query I am writing is not working, here's the code.
CustomerRepository.Find(x => x.ClientId == clientId)
.Where(x => x.CustomerNumber.Contains(lstCustomersInserted.Select(c => c.CustomerNumber)));
Keep it simple:
var lstCustomerNumbers = lstCustomersInserted.Select(c => c.CustomerNumber);
var res = CustomerRepository.Where(x => x.ClientId == clientId && lstCustomerNumbers.Any(c => c == x.CustomerNumber));
I think you have it backwards. Try reversing the Contains.
Edit: I switched to using the generic predicate Exists instead of Contains based on the comment, so you can match a property.
CustomerRepository.Find(x => x.ClientId == clientId)
.Where(x => lstCustomersInserted.Exists(c => x.CustomerNumber == c.CustomerNumber));
How about an Except?
CustomerRepository.Select(x => x.ClientID)
.Except(lstCustomersInserted.Select(x => x.CustomerID));
This will return the IDs of the objects in the repo that don't exist in your lstCustomersInserted.

Categories

Resources