I have the following database query where I am trying to check if there exists an item with a particular barcode that is linked to a particular mailbag. The query is as follows:
var exists = await dbcontext.Items
.Include(t => t.MailBagItems)
.ThenInclude(mt => mt.MailBag)
.AnyAsync(t => t.Barcode.Equals(barcode) &&
t.MailBagItems.FirstOrDefault() != null &&
t.MailBagItems.FirstOrDefault().MailBag.Number.ToLower().Equals(mailbagNumber.ToLower()));
For some reason, I'm getting the following exception:
System.InvalidOperationException: The LINQ expression 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().
I know for a fact from removing parts of the boolean expression that the issue is in the last boolean condition where I'm checking the mailbag number. However, I get the same error if I remove the calls to ToLower(). Can someone indicate what is wrong with my expression and how to fix it? Please note I'm using .NET core 3 and SQL Server.
Managed to make the query work by changing it to the following:
var exists = dbcontext.Items
.AnyAsync(t => t.Barcode.Equals(barcode) &&
t.MailBagItems.Any(t => t.MailBag.Number.ToLower().Equals(mailbagNumber.ToLower())));
Seems it wasn't enjoying the .FirstOrDefault().MailBag before.
Your AnyAsync is to complex for EF to transform to SQL, if you want to still use that query you will have to materialize the entities first, like this:
var exists = dbcontext.Items
.Include(t => t.MailBagItems)
.ThenInclude(mt => mt.MailBag)
.ToListAsync()
.AnyAsync(t => t.Barcode.Equals(barcode) &&
t.MailBagItems.FirstOrDefault() != null &&
t.MailBagItems.FirstOrDefault().MailBag.Number.ToLower().Equals(mailbagNumber.ToLower()));
Also you are missing the await keyword, or was that intended?
Related
This is my Custom filter(Func) to pass in where clause
Func<Project,bool> filter = f =>
{
bool filteredContent = true;
if (!CreatorId.Equals(0))
filteredContent = f.CreatedBy.Equals(CreatorId);
if (filteredContent && !VerticalMarketId.Equals(0))
filteredContent = f.VerticalMarketsId.Equals(VerticalMarketId);
if (filteredContent && !ProductCategoryId.Equals(0))
filteredContent = f.ProductCategoriesId.Equals(ProductCategoryId);
return filteredContent;
};
This is my code where I get all the projects based on the conditions created in filter expression
getProjects = await _context.Projects.Where(x => x.IsDeleted == false && filter.Invoke(x))// Here I'm getting the exception
.Include(PC => PC.ProjectComments.Where(x => x.IsDeleted == false))
.Include(SP => SP.SharedProjects)
.AsNoTracking().ToListAsync();
Exception:The LINQ expression (DbSet......) 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'.
Can someone tell how can I filter the data using expression in this?
NOTE: I can do ToListAsync() before applying the filter, but it'll get all the records from DB then filter on client side. But I want to filter the data on server side.
IF you were using Linq To Objects that should work but you are doing Linq To SQL and in this case you must think on how you would translate this function into a valid SQL statement. Question yourself: How could I pass this function call in a SQL Statement? Depending what you do on the body of your expression, you cannot translate it to SQL, you must be simpler sometimes.
Candidate solution
Add PredicateBuilder class on your project. It will give you easily logical operators to you handle expressions.
http://www.albahari.com/nutshell/predicatebuilder.aspx
Try to define an expression and pass it as argument on Where method of your query method chain. For sample (read the comments):
// define a expression with default condition
Expression<Func<Project, bool>> filter = f => !f.IsDeleted;
// check conditions to add new filtes with `And` logical operator
if (!CreatorId.Equals(0))
filter = filter.And(f => f.CreatedBy.Equals(CreatorId));
else if (!VerticalMarketId.Equals(0))
filter = filter.And(f => f.VerticalMarketsId.Equals(VerticalMarketId));
else if (!ProductCategoryId.Equals(0))
filter = filter.And(f => f.ProductCategoriesId.Equals(ProductCategoryId));
// apply the filter on the query and execute it
getProjects = await _context.Projects.Where(filter)
.Include(PC => PC.ProjectComments.Where(x => !x.IsDeleted))
.Include(SP => SP.SharedProjects)
.AsNoTracking()
.ToListAsync();
Note: I didn't test this code and it probably should be fixed somehow!
Important tips on Linq To SQL:
Logical operators are ok and tend to be translated fine to sql;
Where(x => x.Children.Any(j => j.Children.Any())), each Any call generates a subquery on query scope, be careful with it given it can compromise your database performance.
If you just need to check the existence of an item, use queryable.Any(expression).
If you need to check and then do something, prefer using queryable.FirstOrDefault(expression) and check if the result is null before using.
Use paging with .Take(int) and .Skip(int).
Always concrete your queries by calling .ToList(), .ToArray() or async versions of these methods. Avoid passing queryable in the top layers (query can be executed out of the scope you want).
I figured it out by creating a simple Expression as fololows:
private static Expression<Func<Project, bool>> ProjectFilterExpression(
int creatorId,
int verticalMarketId,
int productCategoryId)
{
Expression<Func<Project, bool>> projectFilterExpression = pfe =>
!pfe.IsDeleted
//CreatorId Filter
&& (creatorId.Equals(0) || pfe.CreatedBy.Equals(creatorId))
//Vertical Market Filter
&& (verticalMarketId.Equals(0) || pfe.VerticalMarketsId.Equals(verticalMarketId))
// Product Category Filter
&& (productCategoryId.Equals(0) || pfe.ProductCategoriesId.Equals(productCategoryId));
return projectFilterExpression;
}
Then I call this static method inside my filter method.
var filter = ProjectFilterExpression(CreatorId, VerticalMarketId, ProductCategoryId);
And finally I applied this filter in my LINQ where clause
getProjects = await _context.Projects.Where(filter).AsNoTracking().ToListAsync();
It's working totally fine.
I using asp.net boilerplate and try to filter users via bool flag and permissions.
Here is code of method
private async Task<List<User>> GetAdminUsers()
{
using (UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
{
var adminUsers = await _userManager.Users.Where(x => x.IsFuelAdmin && _permissionChecker
.IsGranted(new UserIdentifier(null, x.Id),
FuelAdministratorPermissions.Pages_Administration_Missing_PreQual_LoanAmount_Email_Notifications))
.ToListAsync();
return adminUsers;
}
}
But it failing with this error
ionHandling.AbpExceptionFilter : The LINQ expression 'DbSet
.Where(u => __ef_filter__p_0 || !(((ISoftDelete)u).IsDeleted) && __ef_filter__p_1 || ((IMayHaveTenant)u).TenantId == __ef_filter__CurrentTenantId_2)
.Where(u => u.IsFuelAdmin)
.Where(u => ___permissionChecker_0.IsGranted(
user: new UserIdentifier(
null,
u.Id
),
permissionName: "Pages.Administration.QualificationChecks.LoanAmount_Email_Notifications"))' 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()
How I can solve this issue?
There is no "IsGranted" function on the db server. You couldn't use IsGranted as a db function.
If you want to use the local C# IsGranted get the adminUsers into a C# local list and filter then for IsGranted:
var adminUsers = await _userManager.Users.Where(x => x.IsFuelAdmin).ToListAsync();
For a full SQL query you have to build a join query with your users over:
userRoleRepository, userPermissionRepository, rolePermissionRepository
Check out the accept answer:
Get all users with specific permission?
You could define additional property - say, IsAdmin - and set it in the runtime before that query.
Assume Entity has columns SomeId, DateAt of respective types int, DateTime.
Let assume I have parameters collection:
{(SomeId_1, DateAt_1),...,(SomeId_N, DateAt_N)} where 1=<N
I want to retrive all rows in Entity table that:
{r: (SomeId_1=r.SomeId AND DateAt_1=r.DateAt) OR .. OR (SomeId_N=r.SomeId AND DateAt_N=r.DateAt)}
Is there a way to write Linq query against DbSet?
_context.Entity....
that will produce query like:
SELECT *
FROM Entity
WHERE ( SomeId_1 = SomeId AND DateAt_1 = DateAt)
OR..
OR (SomeId_N = SomeId AND DateAt_N = DateAt)
How to achieve this using EF Core 3.1?
REMARK:
Using code like below
List<EntityAtPait> pairs = ...
await _context.Entity.
.Where(f => pairs.Any(x =>
x.DateAt == f.DateAt
&& x.SomeId == f.SomeId ))
.ToListAsync())
throws:
System.InvalidOperationException: 'The LINQ expression 'DbSet<Entity>
.Where(f => ...
.Any(x => x.DateAt == f.DateAt && x.SomeId == f.SomeId ))' 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 have a StudentReceipts table which stores ReceiptNo as string(001,002,003,..,099,..).
I want go get the last receiptno details inorder to increment the receiptno for next transaction.
This is what I have tried
var _lastGeneratedRecDetails = _db.StudentReceipts
.AsEnumerable()
.Where(r => r.Status == true
&& EntityFunctions.TruncateTime(r.DueDate.Value) >= _startDate.Date
&& EntityFunctions.TruncateTime(r.DueDate.Value) <= _endDate.Date)
.OrderByDescending(x => Int32.Parse(x.ReceiptNo))
.FirstOrDefault();
But there i am getting the following exception
this function can only be invoked from linq to entities
Any help will be highly appreciated.
By calling .AsEnumerable() you are going from Linq-To-Entities to Linq-To-Object. By calling it, you are also filtering all the results in memory, so you are pulling the whole StudentReceipts table from the database everytime you do that query as it gets executed past the .AsEnumerable() method. The general rule is to try to do as much as you can on the database side:
var _lastGeneratedRecDetails =
_db.StudentReceipts.Where(r => r.Status == true
&& EntityFunctions.TruncateTime(r.DueDate.Value) >= _startDate.Date
&& EntityFunctions.TruncateTime(r.DueDate.Value) <= _endDate.Date)
.AsEnumerable()
.OrderByDescending(x => Int32.Parse(x.ReceiptNo))
.FirstOrDefault();
If you do it like this, you will filter everything in the database and fetch the filtered results. I don't know what type x.ReceiptNo is though, but calling Int.Parse isn't allowed in Linq-To-Entities. You can filter first and then call AsEnumerable to be able to do the parsing and ordering in memory.
In my case, I was re-using a Func / Filter expression that included DbFunctions.TruncateTime in a follow-up processing statement AFTER I had already processed the query in SQL. Removing it cleared the instance of the exception for me.
use and
.AsQueryable()
var _lastGeneratedRecDetails = _db.StudentReceipts
.AsEnumerable().AsQueryable()
Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.
I am getting this error from the below linq query:
List<Something> results = new List<Something>(items
.Where(w => selecteditems.Count == 0 || w.ops.Intersect(selecteditems).Count() > 0)
.ToList()
.OrderBy(a => a.FirstNumber)
.OrderBy(b => b.SecondNumber));
Would the intersect be throwing this error?
The query provider doesn't know how to translate w.ops.Intersect(selecteditems) into a SQL query.
If selecteditems was another query from the same query provider then it may be able to translate them, or if the entire operation were being done in Linq-to-Objects, rather than Linq-to-SQL, then it would be fine.
As per the error message, the only operation it knows how to perform on such an object is Contains. You can re-work your query to use that instead:
.Where(w => selecteditems.Count == 0 ||
w.ops.Any(op => selecteditems.Contains(op)))
That [should] work.