Adding WHERE statements to a LINQ lambda expression - c#

The two tables I'm working with here are built like this:
Users Table PurchaseLog Table
ID| LDAP| FNAME| LNAME| ID| UserID| PurchaseID | LOCATION |TIME
I'm trying to build a query that gets me back each user and the number of purchases they have. The model I'm trying to fill:
public class UserPurchaseCount{
public string LDAP {get; set;}
public int Count {get; set;}
}
This SQL query I wrote appears to be returning the correct results,
Select Users.LDAP, count(*) as Purchases
FROM Users
JOIN PurchaseLog ON PurchaseLog.UserId=Users.ID
WHERE Users.FName NOT LIKE '%name%'
AND PurchaseLog.CreatedDate <= 'some end date'
AND Purchase.CreatedDate >= 'some start date'
GROUP BY Users.LDAP
I suck at lambdas, but I like them and want to get a better understanding of them. Here's what I've got so far:
var items = db.PurchaseLogs
.Join(db.Users, usr => usr.UserId, x => x.ID, (usr, x) => new { usr, x })
.GroupBy(y => new { y.x.LDAP})
//.Where(i => i.CreatedDate >= startDate)
//.Where(i => i.CreatedDate <= endDate)
//.Where(i => i.FName.Contains("Guy1", StringComparison.CurrentCultureIgnoreCase) == false)
.Select(g => new
{
g.Key.LDAP,
Count = g.Count()
})
.ToList();
This lambda expression works. When I uncomment the WHERE clauses, the compiler throws up in my face.
Error 6 'System.Linq.IGrouping<AnonymousType#2,AnonymousType#3>' does not contain a definition for 'FName'...

Don't group before apply the conditions:
var items =db.PurchaseLogs
.Join(db.Users, usr => usr.UserId, x => x.ID, (usr, x) => new { usr, x })
.Where(i => i.user.CreatedDate >= startDate)
.Where(i => i.user.CreatedDate <= endDate)
.Where(i => !i.x.FName.Contains("Guy1"))
.GroupBy(y => new { y.x.LDAP})
.Select(g => new
{
g.Key.LDAP,
Count = g.Count()
})
.ToList();

Related

LINQ Query Multiple Group and count of latest record - Oracle DB

I tried to divided Linq queries into 3 (total, success, fail) but so far "Total" Linq query is working fine. Please help me to get "Success", "Fail" columns (it has mulitple statuses and we have to check the last column of each transaction and destination)
Note: you need to group by ProcessTime, TransactionId, Destination and check last column whether it is success or Fail then apply count (we are using oracle as backend)
LINQ for Total count
var query = (from filetrans in context.FILE_TRANSACTION
join route in context.FILE_ROUTE on filetrans.FILE_TRANID equals route.FILE_TRANID
where
filetrans.PROCESS_STRT_TIME >= fromDateFilter && filetrans.PROCESS_STRT_TIME <= toDateFilter
select new { PROCESS_STRT_TIME = DbFunctions.TruncateTime((DateTime)filetrans.PROCESS_STRT_TIME), filetrans.FILE_TRANID, route.DESTINATION }).
GroupBy(p => new { p.PROCESS_STRT_TIME, p.FILE_TRANID, p.DESTINATION });
var result = query.GroupBy(x => x.Key.PROCESS_STRT_TIME).Select(x => new { x.Key, Count = x.Count() }).ToDictionary(a => a.Key, a => a.Count);
Check this solution. If it gives wrong result, then I need more details.
var fileTransQuery =
from filetrans in context.AFRS_FILE_TRANSACTION
where accountIds.Contains(filetrans.ACNT_ID) &&
filetrans.PROCESS_STRT_TIME >= fromDateFilter && filetrans.PROCESS_STRT_TIME <= toDateFilter
select filetrans;
var routesQuery =
from filetrans in fileTransQuery
join route in context.AFRS_FILE_ROUTE on filetrans.FILE_TRANID equals route.FILE_TRANID
select route;
var lastRouteQuery =
from d in routesQuery.GroupBy(route => new { route.FILE_TRANID, route.DESTINATION })
.Select(g => new
{
g.Key.FILE_TRANID,
g.Key.DESTINATION,
ROUTE_ID = g.Max(x => x.ROUTE_ID)
})
from route in routesQuery
.Where(route => d.FILE_TRANID == route.FILE_TRANID && d.DESTINATION == route.DESTINATION && d.ROUTE_ID == route.ROUTE_ID)
select route;
var recordsQuery =
from filetrans in fileTransQuery
join route in lastRouteQuery on filetrans.FILE_TRANID equals route.FILE_TRANID
select new { filetrans.PROCESS_STRT_TIME, route.CRNT_ROUTE_FILE_STATUS_ID };
var result = recordsQuery
.GroupBy(p => DbFunctions.TruncateTime((DateTime)p.PROCESS_STRT_TIME))
.Select(g => new TrendData
{
TotalCount = g.Sum(x => x.CRNT_ROUTE_FILE_STATUS_ID != 7 && x.CRNT_ROUTE_FILE_STATUS_ID != 8 ? 1 : 0)
SucccessCount = g.Sum(x => x.CRNT_ROUTE_FILE_STATUS_ID == 7 ? 1 : 0),
FailCount = g.Sum(x => failureStatus.Contains(x.CRNT_ROUTE_FILE_STATUS_ID) ? 1 : 0),
Date = g.Min(x => x.PROCESS_STRT_TIME)
})
.OrderBy(x => x.Date)
.ToList();

Sqlite-net linq GroupBy cannot be translated

I am using Xamarin c# linq with sqlite-net pcl (https://github.com/praeclarum/sqlite-net). I found that all my linq groupby cannot be properly translated into SQL. Linq translates the Where clause but not Group By.
In the following example, the fields used in Transaction:
AccountId: int
Amount: double
ICategoryId: int
All these query formats result in SQL without Group By:
select * from "Transaction" where ("DateWithoutTime" <= ?)
var accountBalances1 = _dataService.Connection.Table<Transaction>()
.Where(r => r.DateWithoutTime <= displayDuration.DurationEnd)
.GroupBy(r => r.AccountId)
.Select(g => new
{
Id = g.Key,
Balance = g.Sum(b => b.Amount)
})
.ToList();
var accountBalances2 = _dataService.Connection.Table<Transaction>()
.Where(r => r.DateWithoutTime <= displayDuration.DurationEnd)
.GroupBy(r => r.AccountId,
(key, g) => new
{
Id = key,
Balance = g.Sum(b => b.Amount),
})
.ToList();
var accountBalances3 = (from t in _dataService.Connection.Table<Transaction>()
where t.DateWithoutTime <= displayDuration.DurationEnd
group t by t.AccountId
into g
select new { g.Key, Balance = g.Sum(g => g.Amount) })
.ToList();
To clarify it has nothing to do with double data type, I tried another group by with int data type only:
var maxIECategoryId = _dataService.Connection.Table<Transaction>()
.Where(r => r.DateWithoutTime <= displayDuration.DurationEnd)
.GroupBy(r => r.AccountId,
(key, g) => new
{
Id = key,
IECategoryId = g.Max(b => b.IECategoryId)
})
.ToList();
Similarly, the generated sql does not have group by.
All group by are processed locally. Is there any trick to write a linq group by that can be processed on the server service? or it is a limitation of this implementation of sqlite orm?
Thanks,
Nick

How GroupBy can be used in LINQ?

I have a following code:
var sql = db.Accounts.AsNoTracking()
.Join(db.Customers.AsNoTracking(),
d => d.AccountNr, c => c.CustNr,
(d, c) => new {Accounts = d, Customers = c })
.GroupBy(g => g.Accounts.AccountNr)
.Where(w => w.Accounts.Date == null)
.Select(s => new
{
Company = s.Customers.CompName,
TWQ = s.Customers.TWQ,
AccountNr = s.Accounts.AccountNr,
DocDate = s.Accounts.DocumentDate,
Income = s.Customers.Income
})
.OrderBy(o => o.DocDate);
The issue is that c# underlines the whole WHERE part with an alert saying that: Element IGrouping <string,> has no definition of Accounts and extension method of Accounts can not be found
I don't know where the problem lies. I also tried to use GroupBy in model (instead of using it in the code above) but got the some problem:
var model = (from ss in sql // here I refer to sql outcome I got from the code above
.GroupBy(g => g.AccountNr)
.Skip(page * 15 - 15)
.Take(15)
.AsEnumerable()
select new DocumentsModel
{
Company = s.Customers.CompName,
TWQ = s.Customers.TWQ,
AccountNr = s.Accounts.AccountNr,
DocDate = s.Accounts.DocumentDate,
Income = s.Customers.Income
}).ToList();
At first I would like to say that my English isn't that good.
If you execute an GroupBy, you'll get collection of elements where each element represents a projection over a group and its key.
That's why I execute SelectMany afterwards to work with the model in a normal way.
db.Accounts
.AsNoTracking()
.Join
(
inner: db.Customers.AsNoTracking(),
outerKeySelector: x => x.AccountNr,
innerKeySelector: x => x.CustNr,
resultSelector: (Accounts, Customers) => new
{
Accounts, Customers
}
)
.Where
(
predicate: x => x.Accounts.Date == null
)
.GroupBy
(
keySelector: x => x.Accounts.AccountNr
)
.SelectMany
(
selector: x => x
)
.Select
(
selector: x => new
{
Company = x.Customers.CompName,
TWQ = x.Customers.TWQ,
AccountNr = x.Accounts.AccountNr,
DocDate = x.Accounts.DocumentDate,
Income = x.Customers.Income
}
)

Linq command to group by date - how to group

I am trying to create an Iqueryable method which returns the number of connections to a service for each day. this data is read from a SQL Server database.
Here is ConnectionItem class
public class ConnectionItem
{
public DateTime CreatedDate { get; set; }
public int NumberOfConnections { get; set; }
}
And here is my iqueryable
private IQueryable<ConnectionItem> ListItems(DataContext dataContext)
{
return dataContext.Connections
.Join(dataContext.Configurations,
connections => connections.ConfigID,
config => config.ConfigID,
(connections, config) => new { cx = connections, cf = config })
.Join(dataContext.Users,
config => config.cf.UserID,
users => users.UserID,
(config, users) => new { cf = config, su = users})
.Where(q => q.su.AccountEventID == 123 && q.cf.cx.Successful == true)
.GroupBy(g => g.cf.cx.CreatedDate.ToShortDateString())
.Select(s => new ConnectionItem
{
CreatedDate = ????,
NumberOfConnections = ????
});
}
How do I access the grouped date value and the number of items per group?
Also, is there an easier way to write this kind of statements? I am not 100% sure on how the aliases cx,cf etc work.
Any input is appreciated.
Group by the Date portion of the DateTime objects. The Date property simply drops the time part. You're converting your dates to strings so you're losing the fidelity of a DateTime object.
var eventId = 123;
return dataContext.Connections.Join(dataContext.Configurations,
conn => conn.ConfigID,
cfg => cfg.ConfigID,
(conn, cfg) => new { conn, cfg })
.Join(dataContext.Users,
x => x.cfg.UserID,
u => u.UserID,
(x, u) => new { x.conn, u })
.Where(x => x.conn.Successful && x.u.AccountEventID == eventId)
.GroupBy(x => x.conn.CreatedDate.Date)
.Select(g => new ConnectionItem
{
CreatedDate = g.Key,
NumberOfConnections = g.Count(),
});
The above could be more nicely expressed using query syntax.
var eventId = 123;
return
from conn in dataContext.Connections
join cfg in dataContext.Configurations on conn.ConfigID equals cfg.ConfigID
join u in dataContext.Users on cfg.UserID equals u.UserID
where conn.Successful && u.AccountEventID == eventId
group 1 by conn.CreatedDate.Date into g
select new ConnectionItem
{
CreatedDate = g.Key,
NumberOfConnections = g.Count(),
};
The .GroupBy linq method returns an IGrouping<TKey, TValue>, which is basically a List with a Key property that you've just grouped by.
So here
Select(s => new ConnectionItem
{
CreatedDate = ????,
NumberOfConnections = ????
});
Your iterating through a IEnumerable<IGrouping<TKey,TValue>>so you can do this
Select(s => new ConnectionItem
{
CreatedDate = s.Key
NumberOfConnections = s.Count()
});
edited as per comment I realized your looking for the number not an actual list
Just call s.Key and s.Count() ,You can get it like:
private IQueryable<ConnectionItem> ListItems(DataContext dataContext)
{
return dataContext.Connections
.Join(dataContext.Configurations,
connections => connections.ConfigID,
config => config.ConfigID,
(connections, config) => new {cx = connections, cf = config})
.Join(dataContext.Users,
config => config.cf.UserID,
users => users.UserID,
(config, users) => new {cf = config, su = users})
.Where(q => q.su.AccountEventID == 123 && q.cf.cx.Successful == true)
.GroupBy(g => g.cf.cx.CreatedDate.ToShortDateString())
.Select(s => new ConnectionItem
{
CreatedDate = s.Key,
NumberOfConnections = s.Count()
});
}
The group clause returns a sequence of IGrouping
objects that contain zero or more items that match the key value for
the group. For example, you can group a sequence of strings according
to the first letter in each string. In this case, the first letter is
the key and has a type char, and is stored in the Key property of each
IGrouping object. The compiler infers the type of the
key.
Group clause docs

Linq - Get Max date from resultset

I need to convert the following SQL query to Linq :-
SELECT CODE,SCODE,MAX(SDATE) AS SDATE FROM SHIFTSCHEDULE
WHERE COMPANY = 'ABC'
GROUP BY CODE,SCODE
ORDER BY MAX(SDATE)
DESC
So far, I have tried this :-
var data = ctx.ShiftSchedule.Where(m =>
m.Company == company && m.EmployeeId == item.EmployeeId
)
.GroupBy(m =>
new
{
m.EmployeeId,
m.ShiftId
})
.Select(m =>
new
{
EmployeeId = m.Key.EmployeeId,
ShiftCode = m.Key.ShiftId,
ShiftDate = m.Max(gg => gg.ShiftDate)
}).ToList();
The results i get are :-
Now what i want is to get record or item in this result set which is MaxDate. In the above image the MaxDate is 1st record.
How to get the MAXDATE from the resultset?
This should work:-
var data = ctx.ShiftSchedule.Where(x => x.Company == company
&& x.EmployeeId == item.EmployeeId)
.GroupBy(x => new { x.CODE, x.SCODE })
.Select(x => new
{
CODE = x.Key.CODE,
SCODE = x.Key.SCODE,
SDATE = x.Max(z => z.SDATE)
})
.OrderByDescending(x => x.SDATE).FirstOrDefault();
You can order the resulting collection and fetch the first object using FirstOrDefault.
If you want just MAXDATE, you can only project that.
Just add .OrderByDescending(x => x.ShiftDate).First(); at the end.
OrderByDescending date and then take .First()
var data = ctx.ShiftSchedule.Where(m =>
m.Company == company && m.EmployeeId == item.EmployeeId
)
.GroupBy(m =>
new
{
m.EmployeeId,
m.ShiftId
})
.Select(m =>
new
{
EmployeeId = m.Key.EmployeeId,
ShiftCode = m.Key.ShiftId,
ShiftDate = m.Max(gg => gg.ShiftDate)
}).ToList().OrderByDescending(x => x.ShiftDate).First();

Categories