I'm trying to clean up my code a little by creating an extension method to generically handle filtering.
Here is the code I'm trying to clean.
var queryResult = (from r in dc.Retailers select r);
if (!string.IsNullOrEmpty(firstName))
queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(firstName.Trim(), ex.FirstName.Trim()) > 0);
if (!string.IsNullOrEmpty(lastName))
queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(lastName.Trim(), ex.LastName.Trim()) > 0);
if (!string.IsNullOrEmpty(companyName))
queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(companyName.Trim(), ex.CompanyName.Trim()) > 0);
if (!string.IsNullOrEmpty(phone))
queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(phone.Trim(), ex.Phone.Trim()) > 0);
if (!string.IsNullOrEmpty(email))
queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(email.Trim(), ex.Email.Trim()) > 0);
if (!string.IsNullOrEmpty(city))
queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(city.Trim(), ex.City.Trim()) > 0);
if (!string.IsNullOrEmpty(zip))
queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(zip.Trim(), ex.Zip.Trim()) > 0);
if (!string.IsNullOrEmpty(country))
queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(country.Trim(), ex.Country.Trim()) > 0);
if (!string.IsNullOrEmpty(state))
queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(state.Trim(), ex.State.Trim()) > 0);
It's clearly very repetitious. So I tried to create an extension method that filtered by a property using reflection. Here is the method.
public static void FilterByValue<T>(this IQueryable<T> obj, string propertyName, string propertyValue)
{
if (!string.IsNullOrEmpty(propertyValue))
{
obj =
obj.Where(
ex =>
SqlFunctions.PatIndex(propertyValue.Trim(), (string)typeof(T).GetProperty(propertyName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase).GetValue(ex)) > 0
);
}
}
and it's to be called like so:
var queryResult = (from r in dc.Retailers select r);
queryResult.FilterByValue("firstname", firstName);
But, I get an error when the linq executes, stating that "GetValue" isn't recognized in linq to entity.
So, is there any other way to clean this up, or do I have to leave it ugly?
LINQ to Entities provides Language-Integrated Query (LINQ) support that enables developers to write queries against the Entity Framework conceptual model using Visual Basic or Visual C#. Queries against the Entity Framework are represented by command tree queries, which execute against the object context.
No. Linq as in query over the data-in-memory that was loaded using your stored procedures (which means that your queries won't be translated to SQL)?
Some of the situations when reflections are useful in C# are given as follows: Reflections are quite useful for creating new types at runtime. It is easy to use reflection with the program metadata attributes. Reflection is needed to examine and instantiate types in an assembly.
Dapper Plus LINQ DynamicYou can execute query dynamically through the Eval-Expression.NET library. The Eval-Expression.NET library can be activated with the Dapper Plus license.
Technically, yes, you could do it, but you'd need to construct the Expression
yourself to pass to Where
.
That said, rather than accepting the property as a string value you should consider instead accepting an Expression<Func<T, string>>
as a parameter so that you have compile time support for verifying that the selected object is valid.
We'll start out with an expression that represents the generic portion; it'll represent a function with*two* parameters, the object and the value of the given property. We can then replace all instances of that second parameter with the property selector that we've defined in the actual method's parameters.
public static IQueryable<T> FilterByValue<T>(
this IQueryable<T> obj,
Expression<Func<T, string>> propertySelector,
string propertyValue)
{
if (!string.IsNullOrEmpty(propertyValue))
{
Expression<Func<T, string, bool>> expression =
(ex, value) => SqlFunctions.PatIndex(propertyValue.Trim(),
value.Trim()) > 0;
var newSelector = propertySelector.Body.Replace(
propertySelector.Parameters[0],
expression.Parameters[0]);
var body = expression.Body.Replace(expression.Parameters[1],
newSelector);
var lambda = Expression.Lambda<Func<T, bool>>(
body, expression.Parameters[0]);
return obj.Where(lambda);
}
else
return obj;
}
And this method uses a function to replace all instances of one expression with another in a given expression. The implementation of that is:
public class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
If you really want to accept the property name as a string then just replace the definition of newSelector
with the following:
var newSelector = Expression.Property(expression.Parameters[0], propertyName);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With