Lambda Func делегат с использованием интерфейса - PullRequest
1 голос
/ 10 ноября 2009

У меня есть класс, реализующий интерфейс, и мне нужно вернуть список Queryable <> этого интерфейса, учитывая определенный предикат Where:

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);
    }

}

проблема в том, что он даже не скомпилируется, так как предикат не будет совпадать.

Я уже пытался:

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

Это скомпилируется, но завершится с ошибкой во время выполнения, сказав, что не может создать экземпляр интерфейса

Вторая попытка:

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

Это приведет к ошибке с более загадочной ошибкой: невозможно привести объект типа 'System.Linq.Expressions.MethodCallExpression' к типу SubSonic.Linq.Structure.ProjectionExpression.

Edit:

Ответ от wcoenen работает как надо. Проблема теперь появляется, когда мой AVeryBigList предоставляется SubSonic 3.0. Я получаю исключение из SubSonic при выполнении запроса с помощью 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

Должен ли я понять, что Linq в SubSonic не поддерживает Cast <> или это ошибка в SubSonic?

1 Ответ

3 голосов
/ 11 ноября 2009

Методы расширения Where для IEnumerable действительно принимают System.Func, именно так вы пытаетесь передать предикат здесь.

Но вы работаете с IQueryable, а не с IEnumerable. Методы расширения Where для IQueryable используют выражение System.Linq.Expressions.Expression, а не System.Func. Измените тип аргумента предиката следующим образом:

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

В качестве альтернативы, вы можете оставить исходное объявление функции и передать предикат методу Where как x => predicate(x), но это саботирует способность реализации IQueryable анализировать выражение и оптимизировать запрос. (Кстати, именно это и делает Subsonic; он анализирует дерево выражений для генерации оператора SQL.)

Кроме того, вы будете рады узнать, что .Cast<ISomeInterface>() больше не требуется в .NET 4.0 из-за новой поддержки ковариации.

...