1

I am implementing IQueryable, and so far have only implemented an expression visitor for 'Where' calls and everything else is currently unsupported. The expression is translated into native T-SQL. I plan on adding support for additional method calls over time, of course.

protected override Expression VisitMethodCall(MethodCallExpression m)
    {

        if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Where")
        {

            sb.Append("SELECT * FROM (");

            this.Visit(m.Arguments[0]);

            sb.Append(") AS T WHERE ");

            LambdaExpression lambda = (LambdaExpression)StripQuotes(m.Arguments[1]);

            this.Visit(lambda.Body);

            return m;

        }

        throw new NotSupportedException(string.Format("Method '{0}' is not supported", m.Method.Name));

    }

Being that my 'Where' support uses deferred execution -- my question is whether it is possible and/or good practice to add support for other methods, such as 'Select', where under the hood deferred execution is not used, but its transparent to whomever is using the IQueryable. So there is a working implementation until a deferred solution is available.

For instance:

var _query = dbContext.Products
  .Where(x => x.ProductName == "") // deferred execution
  .Select(x => new { ... });       // cast ToList() under the hood and proceed

So I guess my question is two-fold,

1) Is it possible / how easy is it to implement?

2) Is it even a good idea?

Thanks.

Sean Thoman
  • 7,429
  • 6
  • 56
  • 103

1 Answers1

1

When something in Linq-To-Sql is not translatable to SQL it throws an exception. The programmer then has to consider that, and modify the method chain to include a call to .AsEnumerable first before calling such methods. IMO, that is more clear than doing so implicitly without the programmer knowing (by calling .ToList under the hood).

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • Where would the call to .AsEnumerable go? Under the VisitMethodCall method? I thought it might need to go at a different level in the chain but I'm not sure. – Sean Thoman Aug 30 '11 at 00:19
  • @Sean, not sure what your'e asking. When I was mentioning `.AsEnumerable`, I was referring to what the programmer would have to type. For example, they might initially have, `dbContext.Products.Where(...).Select(...)` and get the exception. Then the programmer would change it to `dbContext.Products.Where(...).AsEnumerable().Select(...)`. Is that what you're asking about? – Kirk Woll Aug 30 '11 at 01:06
  • Essentially, yes, but is it possible to create an IQueryable implementation where the programmer doesn't have to write AsEnumerable(). By doing Select() you would implicitly be casting to IEnumerable(), and hence a call to Select() would also return a List or IEnumerable and not an IQueryable. – Sean Thoman Aug 30 '11 at 03:50
  • @Sean, yes, it is possible. I thought you were asking whether it was advisable. (and IMO, it isn't) Where that logic needs to go depends on how you are implementing your queryable. Presumably you are visting the entire expression tree and building up SQL as you go (or perhaps at the end). You need to detect invocations to `Select` and handle that in your query provider. Presumably you'll do this by first invoking the SQL that you had constructed (presumably in `Execute(Expression)`) and then, upon the results (possibly in `GetEnumerator`) pass it through your call to `.ToList`. – Kirk Woll Aug 30 '11 at 18:56
  • But if that is the real heart of your question, you may want to consider re-asking it -- I think the community probably thought it was a question soliciting advice on whether to do it or not, rather than a question on how to do it. If the latter, you'll get more eyeballs asking the question again (but phrased differently, of course). – Kirk Woll Aug 30 '11 at 18:57