Альтернатива использованию IList.Contains (item.Id) в Entity Framework для больших списков? - PullRequest
5 голосов
/ 18 июля 2011

Есть ли альтернатива использованию .Contains() для выбора объектов в Entity Framework, которые существуют в указанном списке? Contains() отлично работает, если у вас маленький список, но как только вы начинаете получать несколько тысяч предметов, производительность ужасна.

return (from item in context.Accounts
        where accountIdList.Contains(item.AccountId)
        select item).ToList();

Я использую EF 4.0, .Net Framework 4.0 и SQL Server 2005. Я также не против решения SQL, так как запрос, который генерирует EF, занимает всего секунду для запуска SQL примерно для 10 тыс. Элементов.

Ответы [ 3 ]

1 голос
/ 18 июля 2011

Я нашел альтернативу, которая выполняется примерно за секунду с использованием хранимой процедуры SQL и строки с разделителями-запятыми для параметра.Намного лучше, чем 5+ минут, которые EF использовал, используя .Contains()

Он запускается из моего кода с использованием следующего:

string commaDelmitedList = string.Join(",", accountIdList);
return context.GetAccountsByList(commaDelmitedList).ToList();

StoredProcedure (упрощенно) выглядит следующим образом:

SELECT *
FROM   Accounts as T1 WITH (NOLOCK)
INNER JOIN (
    SELECT Num FROM dbo.StringToNumSet(@commaDelimitedAccountIds, ',')
) as [T2] ON [T1].[AccountId] = [T2].[num]

А пользовательская функция dbo.StringToNumSet() выглядит так:

CREATE FUNCTION [dbo].[StringToNumSet] (
 @TargetString  varchar(MAX),
 @SearchChar varchar(1)
 )
RETURNS @Set TABLE (
 num int not null
 )
AS
BEGIN
 DECLARE @SearchCharPos  int,  @LastSearchCharPos int
 SET @SearchCharPos = 0
 WHILE 1=1
 BEGIN
  SET @LastSearchCharPos = @SearchCharPos
  SET @SearchCharPos = CHARINDEX( @SearchChar, @TargetString, @SearchCharPos + 1 )
  IF @SearchCharPos = 0
  BEGIN
   INSERT @Set( num ) VALUES ( SUBSTRING( @TargetString, @LastSearchCharPos + 1, DATALENGTH( @TargetString ) ) )
   BREAK
  END
  ELSE
   INSERT @Set( num ) VALUES ( SUBSTRING( @TargetString, @LastSearchCharPos + 1, @SearchCharPos - @LastSearchCharPos - 1 ) )
 END
 RETURN
END
0 голосов
/ 18 июля 2011

Contains уже переведено в массивную WHERE IN инструкцию SQL, так что это на самом деле не проблема. Однако вам не следует с нетерпением оценивать запрос, так как он будет выполняться каждый раз, когда вы вызываете этот метод. Воспользуйтесь преимуществами природы linq-to-entity и позвольте запросу оцениваться, когда вы фактически выполняете его.

0 голосов
/ 18 июля 2011

Было бы целесообразно просто прочитать вашу информацию в память и затем выполнить поиск.

Я обнаружил, что в большинстве случаев вам нужно работать с большими объемами данных, если вы можете уйти с чтениявсе данные в память, а затем выполнять поиск гораздо быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...