I try to run this code here, in ASP.NET Entity Framework 7. The target is to have the most efficient solution here which should be a JOIN if the database.
public async Task<List<Building>> GetAllAsync(string commaSeparatedBuildingIDs)
{
var buildingRefIDs = commaSeparatedBuildingIDs.Split(",").ToList();
return await semiSoftDbContext.Buildings
.Join(buildingRefIDs, building => building.ReferenceId, refID => refID, (building, id) => building)
.ToListAsync();
}
I get the following error:
System.InvalidOperationException: The LINQ expression 'DbSet()
.Join(
inner: __p_0,
outerKeySelector: building => building.ReferenceId,
innerKeySelector: refID => refID,
resultSelector: (building, id) => building)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.`
I already have tried several variations of join and read through the internet, but it says that ASP.Net should work with Join().
You should not join here (assuming you are using Entity Framework - AFAIK it does not handle quite a lot of operations with local collections, like joins), use Where with Contains:
return await semiSoftDbContext.Buildings
.Where(b => buildingRefIDs.Contains(b.ReferenceId))
.ToListAsync();
Related
I want to check if a list contains any item from another list using EF Core with Npsql provider. Then I want to get the exact item that was matched in my Dto.
My code is the following (note: Reports.Models is List<string> and so is request.Models as well. The request is consumer filter/search):
var x = await _dbContext.Reports
.Where(x => x.Models.Any(i => request.Models.Contains(i)))
.Select(x => new ReportDto
{
// Model = x.Identifiers.First(i => request.Identifiers.Contains(i)) // this also fails.
Model = request.Models.First(i => request.Models.Any(y => y == i)), // fails on this line
})
.ToListAsync(cancellationToken);
I tried both ways using Any and Contains, neither work. They both return the same error which says:
System.InvalidOperationException: The LINQ expression 'i => __request_Models_0 .Contains(i)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
But I don't understand why? The error never changes, even in my case when I use Any, it still complains about Contains.
You are effectively trying to push your request.Models to the database Server for it to evaluate if any of its datasets are in it. That won't work.
You need to request the Models from the database first and compare them locally OR transform your request.Models into a set of IDs that the database can compare against.
I have a Movie class. It has a Genres property of type List<string>. I use this exact EF Core Value Conversion to store it in the database as a comma-separated string.
In my method, I have movies, which is of type IQueryable<Movie>. The method receives genres as a List<string>, and I want to filter the movies according to the genres.
When I apply this filter, this query fails to translate to the database.
var genre = "Drama";
movies = movies.Where(m => m.Genres.Contains(genre));
The filter works if I apply .ToListAsync() to movies and pull all the movies to the client-side. But I'm trying to find a way to do this on the database-side.
I've also tried these variations:
movies = movies.Where(m => m.Genres.Any(g => g.Contains(genre)));
movies = movies.Where(m => m.Genres.Any(g => g == genre));
I'm pasting in the error message below:
.Where(m => m.Genres
.Contains(__genre_0))' could not be translated. Additional information: Translation of method 'System.Linq.Enumerable.Contains' failed. If this method can be mapped to your custom function, see https://go.microsoft.com/fwlink/?linkid=2132413 for more information. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
If you want to reproduce it on your computer:
Clone this github repository (filter-translation-issue branch)
Put a breakpoint on SearchMoviesExtendedQuery.cs, line 53.
Run the project(API should be the startup project), it will create the FreeFlixDatabase SQL Server database and seed ten movies, then it will open Swagger UI.
In the Swagger UI, run the /api/catalog/MoviesSearch POST method with the message body: {"genres":["string"]}
Approach 1: As mentioned by John above, you can cast to a string an compare in that way:
queryable.Where(x => ((string)(object)p.ConvertibleProperty) == "sup")
Approach 2:
I see that this cannot be done directly on the db-side right now. But as you mention, it can be done on the server-side (you called this client side) after a ToListAsync().
var r = _context.TblExample.Where(w => w.Filter == filter);
if (await r.AnyAsync())
{
var rs = await r.ToListAsync();
var w = rs.Where(w => w.Categories.Contains(category));
return w.ToList();
}
Another approach: Not sure of your exact requirements, but you could also consider a timed job to sync the data from this table into another table specially structured for filtering.
I am working on a C# application. I have to create a predicate for filtering data. I have a model class TissueItem which has a property named ExpirationData. I have to get that data from TissueItem where expiration date is less than or equal to number specified. My predicate expression is:
ExpressionStarter<TissueItem> predicate = PredicateBuilder.New<TissueItem>();
predicate = predicate.And(x =>
Convert.ToInt32((x.ExpirationDate.Value - DateTime.Today).TotalDays)
<= inventorySearchFilterModel.ExpirationStatus);
When i pass this predicate to get the data, i am getting the following exception:
The LINQ expression 'DbSet<TissueItem>()
.Where(t => Convert.ToInt32((t.ExpirationDate.Value - DateTime.Today).TotalDays) <= __inventorySearchFilterModel_ExpirationStatus_0)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
What can be the possible issue. I am using .NET Core 3.1.
Try this variant. Looks like EFC do not handle Timestamp.TotalDays.
var predicate = PredicateBuilder.New<TissueItem>();
predicate = predicate.And(x =>
EF.Functions.DateDiffDay(x.ExpirationDate.Value, DateTime.Today)
<= inventorySearchFilterModel.ExpirationStatus);
In .Net framework I had this working query:
IEnumerable<Cars> LatestCars = await _context.Cars.Where(x => x.IsActive == true && x.IsDeleted == false)
.GroupBy(y => y.ManufacturerId)
.Select(z =>
z.OrderByDescending(k => k.ReleaseDate)
.FirstOrDefault()
)
.OrderByDescending(l => l.ReleaseDate)
.Take(5)
.ToListAsync();
This basicly gets the latest 5 cars released by distinct manufacturers.
But when I switched to .NET Core. This query is not working anymore. And I have this error when I run it:
System.InvalidOperationException: The LINQ expression '(GroupByShaperExpression:
KeySelector: (g.ManufacturerId),
ElementSelector:(EntityShaperExpression:
EntityType: Cars
ValueBufferExpression:
(ProjectionBindingExpression: EmptyProjectionMember)
IsNullable: False
)
)
.OrderByDescending(p => p.ReleaseDate)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
Do you have any suggestions? Thank you.
EF Core, starting from version 3, throws an exception when an expression could only be executed locally. And actually the problem is not the .OrderByDescending() per se, but the fact that the query contains grouping, and grouping only supports Select() projections that aggregate the result to a scalar. Because only this can be translated into a SQL GROUP BY statement.
There is one handy way of dealing with this. If you have a Manufacturer model which contains an IEnumerable<Car> Cars navigational property, you can start from there, and that leads to a translatable query.
So, in your case this working query could be the following:
Manufacturers.Select(m => m.Cars.OrderByDescending(c => c.ReleaseDate).FirstOrDefault())
.OrderByDescending(c => c.ReleaseDate).Take(5)
The downside is that you can have null values in this list, if a manufacturer has 0 cars, so normally it's a good idea to filter those out, but in this case the OrderByDescending() pretty much mitigates this.
Another possible solution is to query a limited number of items, and then continue working locally:
Cars.OrderByDescending(c => c.ReleaseDate).Take(100).ToList()
.GroupBy(c => c.ManufacturerId).Select(g => g.First()).Take(5)
The downside of this is that you can potentially end up having less than 5 results.
I am migrating from .net core 2.2 to 3.0 and getting error for linq query
var accountList = someOtherList.Select(x => x.AccountNo).ToList();
var collectedAmount = await _dbContext.AllocPayments
.Where(p => accountList.Contains(p.AccountNo))
.SumAsync(x => x.Amount);
This is giving error:
System.InvalidOperationException : The LINQ expression
'DbSet
.Where(a => __accountList_0.Contains(a.AccountNo))
.Sum(a => a.Amount)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client
evaluation explicitly by inserting a call to either AsEnumerable(),
AsAsyncEnumerable(), ToList(), or ToListAsync(). See
https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
I read the documentation and can get it working by adding .ToListAsync() and evaluate it in client/.net, but then why cant this query be evaluated on server/sql server?