programmerboy
programmerboy

Reputation: 377

Using Expression vs Func

I have several autocomplete actions, one of them is listed below. Instead of writing different predicates for each autocomplete Where method, I have created an autoCompletePredicate. Since I have multiple autocompletes I am using Reflection to get the Property which is required for that specific AutoComplete and use that Property in my autoCompletePredicate.

I have following code which is working alright.

static string param1, param2;
static PropertyInfo[] properties;
static PropertyInfo prop1, prop2;

public IHttpActionResult GetAutComplete(string term, string dependent)
{
    int pagerSize = 10;

    properties = new MyObject().GetType().GetProperties();
    prop1 = properties.Where(p => p.Name.ToUpper().Equals("PROP1")).FirstOrDefault();
    prop2 = properties.Where(p => p.Name.ToUpper().Equals("PROP2")).FirstOrDefault();
    param1 = term;
    param2 = dependent;

    return Json(context.MyObject.Where(autoCompletePredicate).Select(r => new { label = r.PROP1 }).Distinct().OrderBy(r => r.label).Take(pagerSize).ToList());
}

Func<MyObject, int, bool> autoCompletePredicate = (GF, index) =>
{
    bool isFound = false;
    string term, dependent;

    term = prop1.GetValue(GF).ToString();
    dependent = prop2.GetValue(GF).ToString();

    var termFound = term.Contains(param1.ToUpper());
    var dependentFound = String.IsNullOrEmpty(param2) ? true : dependent.Contains(param2.ToUpper());

    isFound = termFound && dependentFound;

    return isFound;
};

How can I change this code into Expression. I tried below code which compiled fine but at runtime I got the following error

public static Expression<Func<MyObject, bool>> AutoCompleteExpression()
{
    return r => prop1.GetValue(r).ToString().Contains(param1.ToUpper()) && (String.IsNullOrEmpty(param2) ? true : prop2.GetValue(r).ToString().Contains(param2.ToUpper()));
}

"LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression."

I looked at the following post which makes absolute sense, but I am not sure how I can use that example in my scenario (which is dynamically finding properties using Reflection).

Also, what I would like to know what can be advantage of using Expression vs Func (specially in my case)

Upvotes: 0

Views: 755

Answers (1)

Mrinal Kamboj
Mrinal Kamboj

Reputation: 11478

You are trying to trying to execute Contains method on string and want that to be represented in the ExpressionTrees, following is the code you need:

Create String Extension method - Contains: (Case Insensitive)

public static class StringExtensions
{
    public static bool Contains(this string source, string toCheck)
    {
        return source.IndexOf(toCheck, StringComparison.OrdinalIgnoreCase) >= 0;
    }
}

Create the AutoCompleteExpression method as follows, it returns Func<MyObject, bool>:

public static Func<MyObject, bool> AutoCompleteExpression()
{
   // Create ParameterExpression
   ParameterExpression parameterType = Expression.Parameter(typeof(MyObject), "object");

   // Create MemberExpression for Columns
   MemberExpression typeColumnProp1 = Expression.Property(parameterType, "PROP1");
   MemberExpression typeColumnProp2 = Expression.Property(parameterType, "PROP2");

   // Create MethoIndo
   MethodInfo containsMethodInfo = typeof(StringExtensions).GetMethod("Contains",new[] { typeof(string), typeof(string) },null);    

   // Create ConstantExpression values
   ConstantExpression constant1 = Expression.Constant(param1, typeof(string));
   ConstantExpression constant2 = Expression.Constant(param2, typeof(string));

   // Expression for calling methods
   MethodCallExpression expression1 = Expression.Call(null, containsMethodInfo, typeColumnProp1, constant1);
   MethodCallExpression expression2 = Expression.Call(null, containsMethodInfo, typeColumnProp2, constant2);

   // Combine `MethodCallExpression` to create Binary Expression
   BinaryExpression resultExpression = Expression.And(expression1,expression2);

    // Compile Expression tree to fetch `Func<MyObject, bool>`
   return Expression.Lambda<Func<MyObject, bool>>(resultExpression, parameterType).Compile();
 }

It is possible to add lot more flexibility by defining custom extension methods and combining expressions using And / Or

Upvotes: 1

Related Questions