Linq – Lambda Func delegate using interface

linqsubsonic

I have a class implementing an interface, and I need to return a Queryable<> list of that interface, given a certain Where predicate :

public interface ISomeInterface
{
     int ID{get;}
     IQueryable<ISomeInterface> GetWhere(Func<ISomeInterface,bool> predicate);

}
public class SomeClass : ISomeInterface
{
   public static IQueryable<SomeClass> AVeryBigList;
   public int ID {get;set;}
   public IQueryable<ISomeInterface> GetWhere(Func<ISomeInterface,bool> predicate)
    {
      return (from m in AVeryBigList select m).Where(predicate);
    }

}

problem is , this won't even compile, as the predicate won't match.

I've attempted so far:

      return (from m in AVeryBigList select m as ISomeInterface)
             .Where(predicate);

This will compile, but will fail at runtime, saying that it can't instantiate an interface

Second attempt:

  return (from m in AVeryBigList select m)
         .Cast<ISomeInterface>
         .Where(predicate);

This will fail with a more enigmatic error: Unable to cast object of type 'System.Linq.Expressions.MethodCallExpression' to type 'SubSonic.Linq.Structure.ProjectionExpression'.

Edit:

The answer from wcoenen works as it should. Problem now appears when my AVeryBigList is provided by SubSonic 3.0. I get an exception thrown from within SubSonic when executing a query with Cast<>:

Unable to cast object of type 'System.Linq.Expressions.MethodCallExpression' to type 'SubSonic.Linq.Structure.ProjectionExpression'.

SubSonic.Linq.Structure.DbQueryProvider.Translate(Expression expression) at SubSonic.Core\Linq\Structure\DbQueryProvider.cs:line 203

Should I understand that SubSonic's Linq does not support Cast<> or is this a bug in SubSonic?

Best Answer

The Where extension methods for IEnumerable indeed take a System.Func, which is how you are trying to pass the predicate here.

But you're working with IQueryable, not IEnumerable. The Where extension methods for IQueryable take a System.Linq.Expressions.Expression, not a System.Func. Change the type of the predicate argument like this:

IQueryable<ISomeInterface> GetWhere(Expression<Func<SomeClass, bool>> predicate)
{
   return AVeryBigList.Where(predicate).Cast<ISomeInterface>();
}

Alternatively, you could keep the original function declaration and pass the predicate to the Where method as x => predicate(x), but that would sabotage the ability of the IQueryable implementation to analyze the expression and optimize the query. (That's exactly what Subsonic does by the way; it analyzes the expression tree to generate a SQL statement.)

Also, you'll be glad to hear that the .Cast<ISomeInterface>() is no longer necessary in .NET 4.0 because of the new support for covariance.

Related Topic