Reputation: 231
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.
Upvotes: 5
Views: 5820
Reputation: 231
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.
Upvotes: 4
Reputation: 38608
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.
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:
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.queryable.Any(expression)
.queryable.FirstOrDefault(expression)
and check if the result is null before using..Take(int)
and .Skip(int)
..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).Upvotes: 4