IEnumerable и IQueryable
При вызове методов LINQ мы только создаем запрос. Его непосредственное выполнение происходит, когда мы начинаем потреблять результаты этого запроса. Нередко это происходит при переборе результата запроса в цикле for или при применении к нему ряда методов - ToList или ToArray, а также если запрос представляет скалярное значение, например, метод Count.
В процессе выполнения запросов LINQ to Entities мы может получать два объекта, которые предоставляют наборы данных: IEnumerable и IQueryable. С одной стороны, интерфейс IQueryable наследуется от IEnumerable, поэтому по идее объект IQueryable это и есть также объект IEnumerable. Но реальность несколько сложнее. Между объектами этих интерфейсов есть разница в плане функциональности, поэтому они не взаимозаменяемы.
Интерфейс IEnumerable находится в пространстве имен System.Collections и System.Collections.Generic (обобщенная версия). Объект IEnumerable представляет набор данных в памяти и может перемещаться по этим данным только вперед. Запрос, представленный объектом IEnumerable, выполняется немедленно и полностью, поэтому получение данных приложением происходит быстро.
При выполнении запроса IEnumerable загружает все данные, и если нам надо выполнить их фильтрацию, то сама фильтрация происходит на стороне клиента.
Интерфейс IQueryable располагается в пространстве имен System.Linq. Объект IQueryable предоставляет удаленный доступ к базе данных и позволяет перемещаться по данным как в прямом порядке от начала до конца, так и в обратном порядке. В процессе создания запроса, возвращаемым объектом которого является IQueryable, происходит оптимизация запроса. В итоге в процессе его выполнения тратится меньше памяти, меньше пропускной способности сети, но в то же время он может обрабатываться чуть медленнее, чем запрос, возвращающий объект IEnumerable.
Для примера используем следующую модель и контекст данных:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ApplicationContext : DbContext
{
public DbSet<User> Users { get; set; }
public ApplicationContext()
{
Database.EnsureDeleted();
Database.EnsureCreated();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// добавляем один объект
modelBuilder.Entity<User>().HasData(new User { Id=1, Name="Tom"});
}
}
Возьмем два вроде бы идентичных выражения. Объект IEnumerable:
int id = 1;
IEnumerable<User> userIEnum = db.Users;
var users=userIEnum.Where(p => p.Id > id).ToList();
Здесь запрос будет иметь следующий вид:
SELECT [u].[Id], [u].[Name]
FROM [Users] AS [u]
Фильтрация результата, обозначенная с помощью метода Where(p => p.Id > id)
будет идти уже после выборки из бд в самом приложении.
Чтобы совместить фильтры, нам надо было сразу применить метод Where: db.Users.Where(p => p.Id > id);
Объект IQueryable:
int id = 1;
IQueryable<User> userIQuer = db.Users;
var users=userIQuer.Where(p => p.Id > id).ToList();
Здесь запрос будет иметь следующий вид:
SELECT [u].[Id], [u].[Name]
FROM [Users] AS [u]
WHERE [p].[Id] > 1
Таким образом, все методы суммируются, запрос оптимизируется, и только потом происходит выборка из базы данных.
Это позволяет динамически создавать сложные запросы. Например, мы можем последовательно наслаивать в зависимости от условий выражения для фильтрации:
IQueryable<User> userIQuer = db.Users;
userIQuer = userIQuer.Where(p => p.Id < 7);
userIQuer = userIQuer.Where(p => p.Name == "Tom");
var users = userIQuer.ToList();
В данном случае будет создаваться следующий SQL-запрос:
SELECT [u].[Id], [u].[Name]
FROM [Users] AS [u]
WHERE ([p].[Id] < 7) AND (([u].[Name] = N'Tom')
Что же лучше использовать? Все зависит от конкретной ситуации. Если разработчику нужен весь набор возвращаемых данных, то лучше использовать IEnumerable, предоставляющий максимальную скорость. Если же нам не нужен весь набор, а то только некоторые отфильтрованные данные, то лучше применять IQueryable.
Только полноправные пользователи могут оставлять комментарии. Аутентифицируйтесь пожалуйста, используя сервисы.