I’ve been working on my LinqToExcel open source project over this Christmas vacation, and currently I am trying to allow method calls to be used in the Linq statement like the example below shows

private int GetLargeEmployeeCount()
{
  return 200;
}

public Company[] GetLargeCompanies()
{
  IExcelRepository repo = new ExcelRepository("companies.xls");
  var companies = from c in repo.WorkSheet()
                  where c.EmployeeCount >= GetLargeEmployeeCount()
                  select c;

  return companies.ToArray();
}

Let’s take a quick look at the created ExpressionTree.

expression_tree

Now let’s look at the method that handles getting the value from the GetLargeEmployeeCount() method. The SQLExpressionVisitor class is responsible for converting the ExpressionTree created from the Linq statement into the SQL statement that will be used. SQLExpressionVisitor inherits from the common ExpressionVisitor class to aid in walking through the ExpressionTree.

The pertinent node that deals with the GetLargeEmployeeCount() method is highlighted in blue above. The return value from GetLargeEmployeeCount() is retrieved in VisitMethodCall().

protected override Expression VisitMethodCall(MethodCallExpression m)
{
  if (m.Method.Name == "Where")
  {
    _sql.Append(" WHERE ");
    this.Visit(m.Arguments[1]);
  }
  else if (m.Method.Name != "Select")
  {
    object methodObject = ((ConstantExpression)m.Object).Value;
    object methodValue = m.Method.Invoke(methodObject, GetMethodArguments(m.Arguments));
    _params.Add(new OleDbParameter("?", methodValue));
    _sql.Append("?");
  }
  return m;
}

VisitMethodCall() is responsible for dealing with ExpressionTree nodes that contain method calls. It turns out that the where in a Linq statement is actually a method call, so we append a “WHERE” to the string sql statement. _sql is a private StringBuilder object that is used to construct the sql statement based on the ExpressionTree.

But the code we’re really interested in is contained in the else if code block. The select Linq statement is also a method call and we’re not interested in that, so we ignore it in the else if condition.

Now we’re interested in getting the return value of the method. Let’s take another closer look at the Expression node for the GetLargeEmployeeCount() method.

method_expression

This node contains all the information we need to use reflection to get the method return value. The first thing we need to do is get the object that contains the method, which we can get from the m.Object property with some casting of types.

object methodObject = ((ConstantExpression)m.Object).Value;

Now that we have the method object, we can use reflection to invoke the method and get the return value. I first tried to use type reflection to invoke the method.

object returnValue = methodObject.GetType().InvokeMember(m.Method.Name, BindingFlags.InvokeMethod, null, methodObject, GetMethodArguments(m.Arguments));

However, this only works when the method is public. If it isn’t then a MissingMethodException is thrown. Of course, only being able to use public methods is not acceptable, so we need to use Invoke() on the MethodInfo object to invoke the method.

object returnValue = m.Method.Invoke(methodObject, GetMethodArguments(m.Arguments));

The rest of the code block (_params and _sql) deals with generating the sql from the ExpressionTree.

One last point of interest is the GetMethodArguments(). This is a helper method that takes ReadOnlyCollection<Expression> and returns an array of corresponding objects. You can check it out in the SQLExpressionVisitor class if you’re interested in its implementation.

It turns out that it only takes a couple lines of code to get the return value for a method used in a Linq statement, but I spent a couple hours researching how to do it, so hopefully this article will be of help to others who are creating custom Linq providers.

 | Posted by admin | Categories: Linq, LinqToExcel |