在Entity Framework中为大型列表使用IList.Contains(item.Id)的替代方法?

Rac*_*hel 5 c# linq-to-entities entity-framework sql-server-2005

是否有替代使用.Contains()选择实体框架中存在于指定列表中的对象?Contains()如果您的列表很小,那么效果很好,但是一旦您开始获得几千个项目,性能就会很糟糕.

return (from item in context.Accounts
        where accountIdList.Contains(item.AccountId)
        select item).ToList();
Run Code Online (Sandbox Code Playgroud)

我正在使用EF 4.0,.Net Framework 4.0和SQL Server 2005.我不反对SQL解决方案,因为EF生成的查询只需要一秒钟就SQL运行大约10k项目.

Rac*_*hel 1

我找到了一个替代方案,它使用 SQL 存储过程和参数的逗号分隔字符串在大约一秒内运行。比 EF 使用 5 分钟以上的时间要好得多.Contains()

它是使用以下命令从我的代码运行的:

string commaDelmitedList = string.Join(",", accountIdList);
return context.GetAccountsByList(commaDelmitedList).ToList();
Run Code Online (Sandbox Code Playgroud)

StoredProcedure(简化)如下所示:

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

用户定义的函数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
Run Code Online (Sandbox Code Playgroud)