Get Distinct Entries Based On Specific Column in Entity Framework

只愿长相守 提交于 2021-02-17 05:57:08

问题


I have a SQLite table that contains every test result we've run, and I'm looking to write an entity framework query that returns only the most recent test result per project.

Normally, I'd assume this would be "group by project id, return row with the max updated value," or, alternatively, "sort by date and return first". However, when I try the query, I keep getting Entity Framework "could not be translated" errors.

Here's what I've tried:

results = await _context.Results
            .Include(x => x.Project)
            .AsNoTracking()
            .OrderByDescending(x => x.Updated)
            .GroupBy(x => x.ProjectId, (x, y) => y.First())
            .ToListAsync();

However, I keep receiving errors that the .First() command could not be translated by Entity Framework. Is there something I'm missing (or, alternatively, a better way to write the query that is more entity framework friendly)?

For reference, here's the operation I'm trying to do in normal SQL: https://thoughtbot.com/blog/ordering-within-a-sql-group-by-clause

I'd prefer to do as much as the work on the server as possible, because there are only a small number of projects, but there could be thousands of results, and I'd rather not do client-side filtering if possible.

The application is written for ASP.NET Core 3.1, using Entity Framework Core.

Minor edit: While SQLite is being used for development, the final code will run against SQL Server, hence the desire to do processing server-side.


回答1:


Try with a subquery instead of a grouping. Like this:

results = await _context.Results
            .Include(x => x.Project)
            .AsNoTracking() 
            .Where( r => r.Id == _context.Results.Where( rr => rr.ProjectId == r.ProjectID).Max( rr => rr.Id) )
            .ToListAsync();



回答2:


Your method couldn't be translated to T-SQL, Linq to Entities couldn't recognize it. You can modify the code as below (adding AsEnumerable after AsNoTracking):

.Include(x => x.Project)
.AsNoTracking()
.AsEnumerable()

With AsEnumerable after data is loaded, any further operation is performed using Linq to Objects, on the data already in memory.




回答3:


While this isn't portable, here's how this can be done using SQLite-compatable SQL and Entity Framework:

results = await _context.Results
    .FromSqlRaw("SELECT Results.* FROM (SELECT Id, ProjectId, MAX(Updated) as Updated " +
    "FROM Results GROUP BY ProjectId) as latest_results " +
    "INNER JOIN Results ON Results.Id = latest_results.Id")
    .Include(x => x.Project) //not required for question but useful
    .OrderBy(x => x.Project.Name)
    .AsNoTracking()
    .ToListAsync();

If someone has a way to do this in pure LINQ/EF, but still perform the query server-side, I'll happily mark that as the answer, since this is dependent on the exact SQL dialect used.



来源:https://stackoverflow.com/questions/60896172/get-distinct-entries-based-on-specific-column-in-entity-framework

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!