2016-09-05 2 views
3

Обычно различие между LINQ to SQL и LINQ to Objects не является проблемой (и я думаю, что проблема с get-go - преждевременная оптимизация), но как я могу определить, что происходит?Как определить, будет ли LINQ-запрос LINQ to SQL или LINQ to Objects?

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

+3

Ну, а не LINQ to SQL и LINQ для объектов ... но обычно, если вы имеют 'IEnumerable ', операции будут использовать LINQ to Objects, но если у вас есть 'IQueryable ', операции будут использовать «что-то еще». (Конечно, 'AsQueryable' будет обертывать' IEnumerable 'в' IQueryable ', так что это не 100% ...) Что вы действительно ищете? –

+1

Если это 'IQueryable', относитесь к нему с осторожностью. Вы всегда можете использовать 'AsEnumerable()' для выбора обработки в памяти, но вы должны знать о последствиях (загрузка всех данных в память). – haim770

+0

«если у вас есть IQueryable , операции будут использовать« что-то еще » Что-нибудь еще? И как вы можете точно знать? Ожидаем ли мы, что компьютер будет обрабатывать все эти вещи без нашего полного понимания? –

ответ

3

Это не микро оптимизации, чтобы провести различие между Linq-To-Sql и Linq-To-Objects. Последнее требует, чтобы все данные загружались в память, прежде чем вы начнете ее фильтровать. Конечно, это может быть серьезной проблемой.

Большинство методов LINQ используют отложенное выполнение, что означает, что он просто строит запрос но это еще не выполнено (например, Select или Where). Немногие другие выполняют запрос и материализуют результат в коллекцию в памяти (например, ToLIst или ToArray). Если вы используете AsEnumerable, вы также используете Linq-To-Objects, и после него не генерируется SQL для частей после него, а это значит, что данные должны быть загружены в память (но все еще используются отложенное выполнение).

Итак, рассмотрите следующие два запроса. Первые выбирает и фильтры в базе данных:

var queryLondonCustomers = from cust in db.customers 
          where cust.City == "London" 
          select cust; 

а второй выбирает все и фильтры через Linq-To-Objects:

var queryLondonCustomers = from cust in db.customers.AsEnumerable() 
          where cust.City == "London" 
          select cust; 

Последняя имеет одно преимущество: вы можете использовать любой метод .NET, поскольку он Безразлично» t необходимо перевести на SQL (например, !String.IsNullOrWhiteSpace(cust.City)).

Если вы только что получили то, что является IEnumerable<T>, вы не можете быть уверены, что это фактически запрос или уже объект в памяти. Даже try-cast до IQueryable<T> не скажет вам точно, что это на самом деле из-за AsQueryable-method. Может быть, вы можете попробовать применить его к типу коллекции. Если бросок преуспевает, вы можете быть уверены, что это уже материализовались, но в противном случае он не скажет вам, если он использует Linq-To-Sql или Linq-To-Objects:

bool isMaterialized = queryLondonCustomers as ICollection<Customer> != null; 

по теме: EF ICollection Vs List Vs IEnumerable Vs IQueryable

+1

Может быть, стоит добавить, что 'AsEnumerable()' does not * немедленно * выполняет запрос. Отложенное выполнение по-прежнему сохраняется, поэтому вы можете добавить 'Select()', 'Where()', но они будут перехватываться Linq для объектов * в конце концов *. – haim770

+0

@Protectorone, Нет. Это не то же самое. 'AsEnumerable()' не реализует запрос (см. Http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs.825). – haim770

+0

«вы не можете быть уверены, что это действительно запрос или уже объект в памяти» Мы не можем? В самом деле?? И мы должны быть в порядке с этим? Это заставляет меня чувствовать себя невежественным или ленивым ... –

1

Первое решение приходит в мой ум проверка поставщика запроса.

Если запрос материализован, что означает, что данные загружаются в память, используется EnumerableQuery(T). В противном случае используется специальный поставщик запросов, например, System.Data.Entity.Internal.Linq.DbQueryProvider для сущности.

var materialized = query 
        .AsQueryable() 
        .Provider 
        .GetType() 
        .GetGenericTypeDefinition() == typeof(EnumerableQuery<>); 

Однако выше являются идеальными случаями, потому что кто-то может реализовать пользовательский поставщик запроса ведет себя как EnumerableQuery.

1

У меня был тот же вопрос, по разным причинам.

Судя по вашему названию & Исходное описание (вот почему google search привел меня сюда).

Предварительная компиляция, учитывая экземпляр, реализующий IQueryable, не существует способа узнать реализацию интерфейса.

Во время выполнения вам необходимо проверить свойство поставщика экземпляра, как упомянуто @Danny Chen.

public enum LinqProvider 
{ 
    Linq2SQL, Linq2Objects 
} 

public static class LinqProviderExtensions 
{ 
    public static LinqProvider LinqProvider(this IQueryable query) 
    { 

     if (query.Provider.GetType().IsGenericType && query.Provider.GetType().GetGenericTypeDefinition() == typeof(EnumerableQuery<>)) 
      return LinqProvider.Linq2Objects; 
     if (typeof(ICollection<>).MakeGenericType(query.ElementType).IsAssignableFrom(query.GetType())) 
      return LinqProvider.Linq2Objects; 

     return LinqProvider.Linq2SQL; 
    } 
} 

В нашем случае, мы добавляем дополнительные фильтры динамически, но столкнулся с проблемами, с различной обработкой обработки на различных поставщиков индивидуальной чувствительности/nullreference. Следовательно, во время выполнения мы должны были настроить фильтры, которые мы добавляем на основе типа провайдера, и в итоге добавили этот метод расширения: