I have a one-to-many relationship between a project table and an audit table. I'm trying to pick out from the audit table the latest entry for each project entity.
From what I understand to do this I should be able to sort my audit collection by date before grouping by project id, so that I can select the first entry for each group (project id) to get the latest entry.
But when I run my ef/linq query, the results are not correct and the order-by seems to be ignored - even the generated sql doesn't include the order by statement.
Heres the simple example I've tried.
using (var ctx = new MyDbContext())
{
var audit = from a in ctx.ProjectAudits
orderby a.CreatedDate descending
group a by a.ProjectId into grp
select grp.FirstOrDefault();
var resultsList = audit.ToList();
}
The results always return with the earliest audit entry for each project id and not the latest.
Is there something wrong with this query; am I missing something obvious?
UPDATE
Okay, how about this?
ctx.ProjectAudits
.GroupBy(p => p.ProjectId)
.Select(p => p.OrderByDescending(j => j.CreatedDate).FirstOrDefault())
.ToList();
Don't have VS with me here, but it should theoretically group your records, order them within the group by their creation date, and select the first record from each group.
I think you need to look at a different approach. Instead of ordering, why don't you group and then select the audit with the maximum CreatedDate. I've not tested the following and am just throwing it out there:
var audit = from a in ctx.ProjectAudits
group a by a.ProjectId into grp
select new {
// whatever your other properties are
CreatedDate = grp.Max(i => i.CreatedDate)
};
Or, as most people prefer the method syntax:
var audit = ctx.ProjectAudits
.Where(i => i.CreatedDate == ctx.ProjectAudits
.Max(x => x.CreatedDate));
EDIT - made some changes, and tested with a test class and a List<TestClass> and the above works with that.
Related
I am working on .NET 6 application with entity framework core. I am creating record search query using LINQ where I am expecting to receive List of string. No of string values are not fixed and will varies. How I can use List in LINQ contain?
List<string> Roles = new List<string>() { "Business Analyst", "Business Analysis Lead", "Application Support Analyst" };
var records = (from jobProfile in db.JobProfiles
where jobProfile.Role.Contains(Roles)
select jobProfile).ToList();
Something like this:
var records = (
from jobProfile in db.JobProfiles
where jobProfile.Role.Any(r => Roles.Contains(r.Name))
select jobProfile
).ToList();
or with fluent interface:
var records =
db
.JobProfiles
.Where(jp => jp.Role.Any(r => Roles.Contains(r.Name)))
.ToList();
Roles can be any IEnumerable. EF will convert the method call to an IN clause.
Note that if the source (here db.JobProfiles) stopped being an IQueryable and was instead an IEnumerable, then you would be using an O(n) .Contains call. As long as it's EF you can use an IEnumerable for Roles since .Contains is not actually called in that case, but for LINQ to Objects you would want to make sure that it's a Set of some kind instead.
If Role is a string property rather than an entity, then it's a bit simpler:
var records = (
from jobProfile in db.JobProfiles
where Roles.Contains(jobProfile.Role)
select jobProfile
).ToList();
or with fluent interface:
var records =
db
.JobProfiles
.Where(jp => Roles.Contains(jp.Role))
.ToList();
You need to filter by where your Role-list contains the job profile role:
var records = (from jobProfile in db.JobProfiles
where Roles.Contains(jobProfile.Role)
select jobProfile).ToList();
...or in fluent:
var records = db.JobProfiles
.Where(x => Roles.Contains(x.Role))
.ToList();
Good morning,
I'm having trouble with a EF query. This is what i am trying to do.
First i am pulling a list of ID's like so (List of IDs are found in the included x.MappingAccts entity):
Entities.DB1.Mapping mapping = null;
using (var db = new Entities.DB1.DB1Conn())
{
mapping = db.Mappings.Where(x => x.Code == code).Include(x => x.MappingAccts).FirstOrDefault();
}
Later, i'm trying to do a query on a different DB against the list of Id's i pulled above (essentially a IN clause):
using (var db = new Entities.DB2.DB2Conn())
{
var accounts = db.Accounts.Where(mapping.MappingAccts.Any(y => y.Id == ?????????)).ToList();
}
As you can see i only got part way with this.
Basically what i need to do is query the Accounts table against it's ID column and pull all records that match mapping.MappingAccts.Id column.
Most of the examples i am finding explain nicely how to do this against a single dimension array but i'm looking to compare specific columns.
Any assist would be awesome.
Nugs
An IN clause is generated using a IEnumerable.Contains.
From the first DB1 context, materialize the list of Id's
var idList = mapping.MappingAccts.Select(m => m.Id).ToList();
Then in the second context query against the materialized list of id's
var accounts = db.Accounts
.Where(a => idList.Contains(a.Id))
.ToList();
The only problem you may have is with the amount of id's you are getting in the first list. You may hit a limit with the SQL query.
This will give the list of Accounts which have the Ids contained by MappingAccts
using (var db = new Entities.DB2.DB2Conn())
{
var accounts = db.Accounts.Where(s => mapping.MappingAccts.Any(y => y.Id == s.Id)).ToList();
}
from what I've read, I can use LINQ to first group, then order each Group by using "SelectMany", which is described here: How to group a IQueryable by property 1 but order by property 2?
But this doesn't work for IQueryable I guess.
We basically get a BusinessObject with an Main-Entity and an IEnumable of Entities, so I'd like to first order by the Main-Entity sequence, then by each Name of the Subentities.
So I guess my query would look like this in LINQ to objects:
var qry = GetQueryFromSomeWhere();
qry = qry.OrderBy(f => f.MainEntity.SequenceNumber)
.ThenBy(f => f.SubEntities.SelectMany(f => f.Name));
I could order this Names in the Query-Service, but it should be up the consumer to order the entities as he needs.
Is there a possibility to make this work kindahow without loading all Entities in the Memory?
If I'am correctly understanding you want to sort records inside each group by record Name. I think that you could accomplish this by ordering records before doing a group by, try this code:
var q = from m in MainEntities
join s in SubEntities on m.Id equals s.MainId
orderby m.SequenceNumber, s.Name
group new { m, s } by m into grp
orderby grp.Key.SequenceNumber
select grp;
I have a situation where I have a Job which has multiple tests which run at specific intervals. A job run generates a unique TestRunId which is a GUID, which is used to reference multiple results, basically grouping a particular run with a unique RunId(GUID).
Now the problem is that I need to select unique runs which have been generated, but my LINQ query picks up every run.
I tried something like this
var testRunIds = ((from tests in context.testresults
where tests.JobId == jobId
select new
{
tests.TestRunId
}).GroupBy(t=>t.TestRunId).OrderBy(t=>t.Key).Skip((pagenum - 1) * pagesize).Take(pagesize)).ToList();
But as I said, this query picks up each and every testResult. Not sure how I do this now. I tried Distinct(), but that too didnt work. Sample data below.
Thanks
I believe the problem is that I have multiple TestRunId values as its essentially a grouping. Inorder to achieve what I need, I tried using (got using Linqer)
from Queries in db.TestResult
where
Queries.JobId == 1
group Queries by new {
Queries.TestRunId,
Queries.StartTime,
Queries.EndTime
} into g
orderby
g.Key.TestRunId
select new {
_ID = (int?)g.Max(p => p.Id),
g.Key.TestRunId,
g.Key.StartTime,
g.Key.EndTime
}
But this works only for MSSQL datasource which is essentially a
SELECT max(id)[ ID],
TestRunId,
StartTime,
Endtime
FROM dbo.query where jobid = 1 group by TestRunId,StartTime,Endtime order by StartTime;
But what I need is
SELECT TestRunId,StartTime,Endtime FROM testresult where jobid = 1 group by TestRunId order by StartTime;
for MySQL.
Try this:-
var jobs = context.testresults;
var query2 = jobs.Where(x => x.TestID == 1).OrderBy(x => x.StartTime).Select(x => x.TestRunID).Distinct();
Working Fiddle.
I think you're possibly looking for this:
var testRunIds = context.testresults.Where(t => t.JobId == jobId).OrderBy(t => t.starttime)
.Select(t => t.TestRunId).Distinct().Skip((pagenum - 1) * pagesize).Take(pagesize)
.ToList();
Do the filtering and ordering first, then select the single field needed, then use Distinct() for uniqueness, then skip/take as required. Selecting the single field first then attempting to order or filter on other fields in the table won't work as those fields are no longer part of the query.
Thanks for helping me out. I managed to do this in a two step process.
var testRunIds = (from tests in context.testresults
where tests.JobId == jobId
select new
{
tests.TestRunId,
tests.StartTime
}).OrderBy(x => x.StartTime).Skip((pagenum - 1) * pagesize).Take(pagesize).GroupBy(x=>x.TestRunId).ToList();
var resultData = testRunIds.Select(testRunId => (context.testresults.Where(
items => items.TestRunId == testRunId.Key)).FirstOrDefault()).ToList();
I have 2 SQL statements that basically do the same thing, that is, retrieve the last record from a table based on a datetime field for a group of records. I am using the data-first Entity Framework model. How would I write either of these SQL statements using LINQ Lambda functions?
ie,
var u = db.AccessCodeUsage.Where(...).GroupBy(...)
rather than
var u = from a in db.AccessCodeUsage
where ...
group by ...
SQL Statements:
SELECT *
FROM AccessCodeUsage a
WHERE NOT EXISTS (SELECT 1
FROM AccessCodeUsage
WHERE LocationId = a.LocationId
AND Timestamp > a.Timestamp)
SELECT a.*
FROM AccessCodeUsage a
WHERE a.Timestamp =
(SELECT MAX(Timestamp)
FROM AccessCodeUsage
WHERE a.LocationId = LocationId
AND a.AccessCode = AccessCode
GROUP By LocationId, AccessCode)
If you need to have the method-call form, but are finding it tricky to work out, then use the other syntax first:
from a in db.AccessCodeUsage
orderby a.TimeStamp descending
group a by a.LocationId into grp
from g in grp select g.First();
Then convert to method calls by taking each clause one at a time:
db.AccessCodeUsage
.OrderByDescending(a => a.TimeStamp)
.GroupBy(a => a.LocationId)
.Select(g => g.First());
From which I can workout the second without bothering to write out the linq-syntax form first:
db.AccessCodeUsage
.OrderByDescending(a => a.TimeStamp)
.GroupBy(a => new {a.LocationId, a.AccessCode})
.Select(g => g.First());
(Except it doesn't include what may be a bug, in that if timestamps aren't guaranteed unique, the SQL given in the question could include some extra inappropriate results).
I can't check on the SQL produced right now, but it should hopefully be equivalent in results (if not necessarily matching). There's cases where grouping doesn't translate to SQL well, but I certainly don't think this would be one.
I ended up using the following which corresponds to the first SQL statement.
// Retrieve only the latest (greatest value in timestamp field) record for each Access Code
var last = AccessCodeUsages.Where(u1 => !AccessCodeUsages
.Any(u2 => u2.LocationId == u1.LocationId &&
u2.AccessCode == u1.AccessCode &&
u2.Timestamp > u1.Timestamp));