2015-04-25 1 views
0

Я просто потратил некоторое время, чтобы узнать, как вернуть IQueryable из метода ... и мне все еще интересно, правильно ли это сделать.Возврат IQueryable <T> из моего метода? Правильно ли это?

Вот мой репозиторий класс:

public class CarRepository : ICarRepository 
{ 
    // Fake entities 
    private IList<Car> _entities = new List<Car>() 
    { 
     new Car() { Brand = "Lamborghini", Name = "Huracán"}, 
     new Car() { Brand = "BMW", Name = "X6" } 
    }; 

    // Allows deferred execution/further refinement 
    public IQueryable<Car> FindByQueryable(Expression<Func<Car, bool>> predicate) 
    { 
     var query = _entities.Where(predicate.Compile()).AsQueryable(); 
     return query; 
    } 

    // Returning an IList or IEnumerable 
    public IList<Car> FindBy(Expression<Func<Car, bool>> predicate) 
    { 
     return _entities.Where(predicate.Compile()).ToList(); 
    } 
} 

Сначала я подумал, что что-то подобное, что должно работать, но это не компиляции:

public IQueryable<Car> FindByQueryable(Expression<Func<Car, bool>> predicate) 
    { 
     var query = _entities.Where(predicate); 
     return query; 
    } 

Правильно ли я с predicate.Compile () и .AsQueryable?

Спасибо вам за помощь! Bastien

+1

Я бы скорее вернул 'IEnumerable ' вместо 'IQueryable'. –

+1

@AlexSikilinda IQueryable - [лучший выбор] (http://stackoverflow.com/questions/2876616/returning-ienumerablet-vs-iqueryablet) для баз данных. Однако я не одобряю это в целом, я думаю, что репозиторий, который возвращает IQueryables, имеет низкое значение (возможно, просто используйте напрямую провайдер linq EF/LtSql и т. Д., И ваши абстракции будут течь во дворцах, которых вы не ожидаете (например, в в случае EF, ясно, какие данные отслеживаются?). –

+0

Все в порядке. Оставьте ответственность за слой Dal, который будет взаимодействовать с репозиторием.В слое dal вы вызываете ToList, когда закончите построение полного запроса. – Legends

ответ

3

Поскольку это не имеет смысла. Если вы хотите использовать методы Queryable для удаленных запросов в базе данных, вы должны использовать деревья выражений. Использование Compile преобразует дерево в делегат, что разрушает эту возможность.

_entities должно быть IQueryable<T> для того, чтобы таргетировать Queryable методов.

AsQueryable является запахом кода, который обычно указывает на ошибки, описанные выше. Это поддельный запрос. Он находится в памяти (за исключением того, что исходный код действительно IQueryable, а затем он выполняет бросок).

1

Причина

var query = _entities.Where(predicate); 

терпит неудачу, потому что _entities реализует только IEnumerable<Car>, не IQueryable<Car>. Метод расширения Enumerable.Where для IEnumerable<T> занимает Func<T, bool>. Метод расширения Queryable.Where для IQueryable<T> принимает Expression<Func<T, bool>>.

Вместо того, чтобы вручную компиляции деревьев выражений, вы можете переместить AsQueryable() немного вверх:

var query = _entities.AsQueryable().Where(predicate); 

Как USR в ответ справедливо указывает, это обычно не имеет смысла, так как он будет использовать локальную фильтрацию, никогда ни одна серверная фильтрация. Однако существуют исключения, в которых это имеет смысл, и ваш случай может быть одним из таких исключений: если у вас есть несколько реализаций ICarRepository, некоторые локальные, некоторые удаленные, то это может иметь смысл сделать это так, как вы делаете. Вы не хотите, чтобы пользователь ICarRepository столкнулся с вопросом о том, использовать ли делегаты или деревья выражений: пользователь должен просто использовать деревья выражений, ваш CarRepository может передать эти деревья выражений некоторому поставщику запросов и этот поставщик запросов затем можно выбрать, компилировать ли их делегаты или перевести их на другой язык, например SQL.