C# 在大型列表的实体框架中使用IList.Contains(item.Id)的替代方法?
除了使用C# 在大型列表的实体框架中使用IList.Contains(item.Id)的替代方法?,c#,sql-server-2005,entity-framework,linq-to-entities,C#,Sql Server 2005,Entity Framework,Linq To Entities,除了使用.Contains()选择实体框架中存在于指定列表中的对象之外,还有其他方法吗Contains()如果列表很小,效果会很好,但是一旦您开始获得几千个项目,性能就会很差 return (from item in context.Accounts where accountIdList.Contains(item.AccountId) select item).ToList(); 我正在使用EF4.0、.NETFramework4.0和SQLServer20
.Contains()
选择实体框架中存在于指定列表中的对象之外,还有其他方法吗Contains()
如果列表很小,效果会很好,但是一旦您开始获得几千个项目,性能就会很差
return (from item in context.Accounts
where accountIdList.Contains(item.AccountId)
select item).ToList();
我正在使用EF4.0、.NETFramework4.0和SQLServer2005。我也不反对SQL解决方案,因为EF生成的查询只需一秒钟就可以在SQL上运行大约10000个项目。将信息读入内存然后进行搜索是否可行
我发现,在大多数情况下,如果可以将所有数据读入内存,然后进行查找,那么您就需要处理大量数据。它的查找速度要快得多。
包含的
已被转换为大量的WHERE in
SQL语句,因此这不是一个真正的问题。但是,您不应该急切地评估查询,因为每次调用该方法时都会执行查询。利用linq to实体的特性,并在实际迭代查询时对其进行计算。我发现了一个替代方法,它使用SQL存储过程和参数的逗号分隔字符串在大约一秒钟内运行。比使用.Contains()
它使用以下代码从我的代码运行:
string commaDelmitedList = string.Join(",", accountIdList);
return context.GetAccountsByList(commaDelmitedList).ToList();
StoredProcess(简化)如下所示:
SELECT *
FROM Accounts as T1 WITH (NOLOCK)
INNER JOIN (
SELECT Num FROM dbo.StringToNumSet(@commaDelimitedAccountIds, ',')
) as [T2] ON [T1].[AccountId] = [T2].[num]
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
用户定义的函数dbo.StringToNumSet()如下所示:
SELECT *
FROM Accounts as T1 WITH (NOLOCK)
INNER JOIN (
SELECT Num FROM dbo.StringToNumSet(@commaDelimitedAccountIds, ',')
) as [T2] ON [T1].[AccountId] = [T2].[num]
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
不,数据库有几百万个帐户,我想从数据库中获取的数据包括几个连接,这使得最终结果集更加均匀bigger@Rachel,您是否尝试使用探查器查看正在生成的查询。有一个免费的线索,可以帮助这一点。是的,我有和查询运行良好。从几百万张唱片中提取大约10万张唱片大约需要一秒钟的时间。我觉得不可接受的一点是,EF使用.Contains()在大约10k个项目的列表上生成SQL语句大约需要5分钟。@Rachel,您可以预编译查询,然后它们应该运行得更快。请看一下这个链接。下面是一个如何运行预编译查询的链接。我不确定预编译查询是否会有帮助,因为这不是造成问题的查询,而是
.Contains()
中的10k项列表导致了减速。您可以发布一些代码来显示示例吗?我对linq和实体还是相当陌生的。另外,SQL语句的性能很好(大约需要一秒钟的处理时间),它正在构建花费EF这么长时间的SQL语句。嗯,好的。删除ToList
就是一个例子。虽然我能理解,但这可能需要一些时间。是否可以以某种方式将accountIdList
保存在数据库中?然后,您可以使用简单的联接,而不是自动生成的WHERE IN
子句。accountIdList
基于用户输入,因此不是。它可以从accountid的excel文件中读取,也可以基于其他用户输入(如销售批次、付款批次等)生成