2010-03-18 3 views
7

Я использую LINQ-SQL как свой DAL, у меня тогда есть проект под названием DB, который действует как мой BLL. Затем различные приложения получают доступ к BLL для чтения/записи данных из базы данных SQL.В чем разница между этими запросами LINQ?

У меня есть эти методы в моем BLL для одной конкретной таблицы:

public IEnumerable<SystemSalesTaxList> Get_SystemSalesTaxList() 
{ 
     return from s in db.SystemSalesTaxLists 
       select s; 
} 

public SystemSalesTaxList Get_SystemSalesTaxList(string strSalesTaxID) 
{ 
    return Get_SystemSalesTaxList().Where(s => s.SalesTaxID == strSalesTaxID).FirstOrDefault(); 
} 

public SystemSalesTaxList Get_SystemSalesTaxListByZipCode(string strZipCode) 
{ 
    return Get_SystemSalesTaxList().Where(s => s.ZipCode == strZipCode).FirstOrDefault(); 
} 

Все довольно прямо вперед, я думал.

Get_SystemSalesTaxListByZipCode всегда возвращает нулевое значение, даже если у него есть почтовый индекс, который существует в этой таблице.

Если я пишу метод, как это, он возвращает строку, я хочу:

public SystemSalesTaxList Get_SystemSalesTaxListByZipCode(string strZipCode) 
{ 
    var salesTax = from s in db.SystemSalesTaxLists 
        where s.ZipCode == strZipCode 
        select s; 

    return salesTax.FirstOrDefault(); 
} 

Почему другой метод не возвращает то же самое, как запрос должен быть идентичен?

Обратите внимание, что перегруженный Get_SystemSalesTaxList(string strSalesTaxID) возвращает запись просто отлично, когда я даю ей действительный SalesTaxID.

Есть ли более эффективный способ написать эти классы типа «помощник»?

Спасибо!

+0

Тупой вопрос - уверены ли вы, что вы передаете ZipCode в 'Get_SystemSalesTaxListByZipCode'? – Oded

+1

Проблема в том, что идентифицируется «IEnumerable » - но я просто кричу, чтобы сказать «это, вероятно, это способ жаловаться на венгерскую нотацию» ;-p –

ответ

7

Это, вероятно, по-разному, как LINQ обрабатывает IEnumerable<T> и IQueryable<T>.

Вы указали Get_SystemSalesTaxList как возвращающийся IEnumerable<SystemSalesTaxList>. Это означает, что когда в первом примере кода вы применяете оператор Where к результатам Get_SystemSalesTaxList, он получает разрешение на метод расширения Enumerable.Where. (Обратите внимание, что значение имеет тип объявлен Да, во время выполнения Get_SystemSalesTaxList возвращается в IQueryable<SystemSalesTaxList>, но его объявлен типа. - то, что видит компилятор - это IEnumerable<SystemSalesTaxList>.) Enumerable.Where запускает указанную .NET предикат над объектом последовательность. В этом случае он выполняет итерацию по всем объектам SystemSalesTaxList, возвращаемым Get_SystemSalesTaxList, в результате чего свойства ZipCode равны указанной строке zip-кода (с использованием оператора .NET String ==).

Но в вашем последнем примере кода вы применяете оператор Where к db.SystemSalesTaxList, который объявлен как тип IQueryable<SystemSalesTaxList>. Таким образом, оператор Where в этом примере получает разрешение Queryable.Where, что переводит указанное предикатное выражение в SQL и запускает его в базе данных.

Так что разные в методах почтового индекса - это то, что первый запускает тест C# s.ZipCode == strZipCode в .NET, а второй переводит его в SQL-запрос WHERE ZipCode = 'CA 12345' (параметризированный SQL действительно, но вы получаете идею). Почему они дают разные результаты? Трудно быть уверенным, но предикат C# == чувствителен к регистру, и в зависимости от ваших настроек сортировки SQL может быть или не быть чувствительным к регистру. Поэтому мое подозрение заключается в том, что strZipCode не соответствует почтовым индексам базы данных в случае, но во второй версии сортировка SQL Server сглаживает это.

Лучшим решением, вероятно, является изменение объявления Get_SystemSalesTaxList для возврата IQueryable<SystemSalesTaxList>. Главным преимуществом этого является то, что это означает, что запросы, построенные на Get_SystemSalesTaxList, будут исполняться на стороне базы данных.На данный момент ваши методы оттягивают ВСЕГО в таблице базы данных и фильтруют его на стороне клиента. Изменение объявления приведет к тому, что ваши запросы будут переведены на SQL, и они будут работать намного эффективнее, и, надеюсь, вы решите проблему с почтовым индексом в сделке.

+0

Имеет смысл для меня, и это было точно такое ответ, который я искал, когда отправлял вопрос! Спасибо. – SnAzBaZ

1

Настоящая проблема заключается в использовании IEnumerable<T>, что нарушает «состав» запросов; это имеет два эффекта:

  • вы читаете все (или, по крайней мере, больше, чем нужно) вашей таблицы каждый раз, даже если вы попросите один ряд
  • вы работаете LINQ к объектам правила, так что чувствительность к регистру применяется

Вместо этого, вы хотите использовать IQueryable<T> внутри слоя данных, что позволяет объединить несколько запросов с дополнительными Where, OrderBy, Skip, Take и т.д. по мере необходимости и иметь его построить TSQL, чтобы соответствовать (и использовать свои правила чувствительности к регистру db).

Есть ли более эффективный способ написать эти классы типа «помощник»?

Для получения более эффективной (меньше кода для отладки, не поток всей таблицы, лучше использовать идентичности к карте короткого замыкания дополнительных операций поиска (с помощью FirstOrDefault и т.д.)):

public IEnumerable<SystemSalesTaxList> Get_SystemSalesTaxList() 
{ 
    return db.SystemSalesTaxLists; 
} 

public SystemSalesTaxList Get_SystemSalesTaxList(string salesTaxID) 
{ 
    return db.SystemSalesTaxLists.FirstOrDefault(s => s.SalesTaxID==salesTaxID); 
} 

public SystemSalesTaxList Get_SystemSalesTaxListByZipCode(string zipCode) 
{ 
    return db.SystemSalesTaxLists.FirstOrDefault(s => s.ZipCode == zipCode); 
} 
+0

Хотя см. Также мою заметку здесь (http://stackoverflow.com/questions/2469816/what-is-the-best-way-to-determine-if-table-already-has-record-with-specific-id/ 2469836 # 2469836) для разницы между 'Where (предикат) .FirstOrDefault()' и 'FirstOrDefault (предикат)' между .NET 3.5/3.5SP1/4.0 –

+0

Спасибо, не один, а два замечательных ответа на мой вопрос! – SnAzBaZ

Смежные вопросы