3
I’ve been using Linqkit to create generic queries for a long time.
One thing that always bothered me is that I always have to test if the value sent in the filter is valid.
For example: Suppose I have a string filter. Conditions can be Equal, Startswith, Endswith and Contains.
My method would be more or less the following:
public List<MyModel> Get(MyModelFilter filter)
{
if (string.IsNullOrEmpty(filter.prop))
{
predicate = predicate.And(_myModel => myModel.Prop.Contains(filter.prop));
}
// mais uma gigante quantidade de if's com vários filtros
return DbSet.AsExpandable()
.Where(predicate)
.ToList();
}
To end this lot of If’s, I decided to create a generic method to apply the filter in properties. My idea is to pass the property where the filter will be applied, and the filter definition, and encapsulate the Expression creation logic
It would be something like
public List<MyModel> Get(MyModelFilter filter)
{
predicate = predicate.And(_myModel => myModel.Prop, filter.PropFilterDefinition);
// adeus If's, apenas as outras implementações de filtros
return DbSet.AsExpandable()
.Where(predicate)
.ToList();
}
For this, I have created some extension methods to take care of it
public static Expression<Func<TPredicate, bool>> And<TPredicate>(
this ExpressionStarter<TPredicate> predicate,
Func<TPredicate, string> property, StringFilterDefinition filter,
bool ignoreNull = true)
{
if (InvalidStringFilter(filter, ignoreNull))
{
return predicate;
}
// este é o And do LinqKit
return predicate.And(BuildPredicate(property, filter));
}
private static Expression<Func<TPredicate, bool>> BuildPredicate<TPredicate>(
Func<TPredicate, string> property,
StringFilterDefinition filter)
{
if (filter.Filter == StringFilterComparators.Equal)
{
return x => property.Invoke(x) == filter.Value;
}
if (filter.Filter == StringFilterComparators.BeginsWith)
{
return x => property.Invoke(x).StartsWith(filter.Value);
}
if (filter.Filter == StringFilterComparators.EndsWith)
{
return x => property.Invoke(x).EndsWith(filter.Value);
}
return x => property.Invoke(x).Contains(filter.Value);
}
private static bool InvalidStringFilter(
StringFilterDefinition filter,
bool ignoreNullValue = true)
{
if (filter?.Filter == null)
{
return true;
}
return ignoreNullValue && string.IsNullOrEmpty(filter.Value);
}
The problem is that the filter is not applied, and the answer is in Invoke right up there. EF cannot translate the above expression to SQL. The RU error is
Microsoft.EntityFrameworkCore.Query.Internal.Sqlserverquerycompilationcontextfactory[8] The LINQ Expression '(__property_0.Invoke([x]) == __filter_Value_1)' could not be Translated and will be evaluated locally. To configure this Warning use the Dbcontextoptionsbuilder.Configurewarnings API (Event id 'Relationaleventid.Queryclientevaluationwarning'). Configurewarnings can be used when Overriding the Dbcontext.Onconfiguring method or using Adddbcontext on the application service Provider.
The question is:
How can I make this building work? Also, any suggestions on how to best this?
+1 for the Linqkit =)
– rubStackOverflow