C# EntityFramework:SQL上的Linq:Contains还是IndexOf?

C# EntityFramework:SQL上的Linq:Contains还是IndexOf?,c#,entity-framework,linq-to-sql,entity-framework-6,C#,Entity Framework,Linq To Sql,Entity Framework 6,我对EntityFramework6和.NET4.5(C#)有一种奇怪的情况 我在两个不同的地方提出了(几乎)相同的问题。但一次它查询数据库,第二次它查询内存中的对象。由于我在筛选子字符串,这是一个关键的区别: 数据库结构为表角色,右侧和跨表角色 第一次,我想查找尚未分配给角色的所有可用权限,并添加一个手动过滤器(这就是它变得复杂的原因),以减少结果列表: Role role = ...; string filter = ...; var roleRightNames = role.Right.

我对EntityFramework6和.NET4.5(C#)有一种奇怪的情况

我在两个不同的地方提出了(几乎)相同的问题。但一次它查询数据库,第二次它查询内存中的对象。由于我在筛选子字符串,这是一个关键的区别:

数据库结构为表角色,右侧和跨表角色

第一次,我想查找尚未分配给角色的所有可用权限,并添加一个手动过滤器(这就是它变得复杂的原因),以减少结果列表:

Role role = ...;
string filter = ...;
var roleRightNames = role.Right.Select(roleRight => roleRight.RightName);
var filteredRights = context.Right.Where(right => !roleRightNames.Contains(right.RightName));
if (!string.IsNullOrWhiteSpace(filter))
{
    filteredRights = filteredRights.Where(e => e.RightName.Contains(filter));
}
var result = filteredRights.ToList();
我无法使用
IndexOf(filter,StringComparison.InvariantCultureIgnoreCase)>=0)
,因为这无法转换为SQL。但是我对
Contains
很满意,因为它可以产生所需的结果(见下文)

启用SQL输出时,我得到:

SELECT [Extent1].[RightName] AS [RightName]
FROM [dbo].[Right] AS [Extent1]
WHERE ( NOT ([Extent1].[RightName] IN ('Right_A1', 'Right_A2', 'Right_B1'))) AND ([Extent1].[RightName] LIKE @p__linq__0 ESCAPE '~'
-- p__linq__0: '%~_a%' (Type = AnsiString, Size = 8000)
这正是我想要的,在过滤器“\u a”上进行不区分大小写的搜索,以查找例如“Right\u A3”

第二次要筛选同一筛选器的现有关联权限时:

Role role = ...;
string filter = ...;
var filteredRights = string.IsNullOrWhiteSpace(filter)
    ? role.Right
    : role.Right.Where(e => e.RightName.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) >= 0);            
var result = filteredRights.ToList();
这一次它迫使我使用
IndexOf
,因为它使用
string
Contains
方法,而不是将其转换为类似的SQL
string。Contains
区分大小写

我的问题是,通过查看代码,我无法预测何时对数据库执行查询以及何时在内存中执行查询,因为我无法在第一个查询中使用
IndexOf
,在第二个查询中使用
Contains
,这对我来说似乎有点不可预测。如果有一天第二个查询首先执行,而数据不在内存中,会发生什么情况

编辑2020年2月10日


好的,我知道了主要的区别是什么<代码>上下文。右侧的
类型为
DbSet
,它是一个
IQueryable
,后续的扩展方法
Where
。但是,
userRole.Right
返回一个
ICollection
,它是一个
IEnumerable
,后面的
Where
也是如此。有没有办法使实体对象的relationship属性成为
IQueryable
<代码>AsQueryable不起作用。这意味着在执行内存中的
Where
操作之前,所有相关的
Right
实体总是从数据库中获取。 我们并不是在谈论大量的数据,至少现在这种行为是可以预测的,但我发现这是不幸的

我的问题是,从代码来看,我无法预测 在内存中执行查询时,将对数据库执行查询 由于我不能在第一个查询中使用IndexOf,而在 其次,这对我来说似乎有点不可预测

您可以在这两个查询中使用
IndexOf
Contains
,只要您不使用带有
StringComparison
的重载。正如@BrettCaswell所指出的,大小写匹配是由数据库/表/列的排序规则确定的。 如果查询的根是上下文的
DbSet
,并且所有方法调用都可转换为SQL,则该查询将被转换为SQL

一旦无法转换方法,将在SQL级别执行当前状态请求,并在.Net应用程序的内存中执行查询的其余部分


另外,我认为
p\uu linq\uu 0
值应该是
'%~\u a%'
,因为
LIKE
子句中的一个特殊字符。

好的,所以我找到了两种不同的解决方案,在关系包含巨大结果集的情况下总是查询数据库。这两种解决方案都不是直接直观的-IMHO-您将需要以前不需要的
DbContext
变量

解决方案一是使用
角色
表作为起点,简单地筛选具有正确Id的实体。 注意您不能使用
Single
,因为这样您就可以处理单个实体对象,然后就回到了开始的地方。您需要使用
Where
,然后使用
选择many
,即使这是违反直觉的:

Role role = ...;
string filter = ...;
var filteredRights = context.Role.Where(e => e.RoleId == userRole.RoleId).SelectMany(e => e.Right);
if (!string.IsNullOrWhiteSpace(filter))
{
    filteredRights = filteredRights.Where(e => e.RightName.Contains(filter));
}
var rights = filteredRights.ToList();
这将导致针对数据库的SQL查询:

SELECT 
    [Extent1].[RightName] AS [RightName]
    FROM [dbo].[Role_Right] AS [Extent1]
    WHERE ([Extent1].[RoleId] = @p__linq__0) AND ([Extent1].[RightName] LIKE @p__linq__1 ESCAPE '~')
-- p__linq__0: '42' (Type = Int32, IsNullable = false)
-- p__linq__1: '%~_a%' (Type = AnsiString, Size = 8000)
我在这里找到的第二个解决方案是:

就我而言,这将导致:

Role role = ...;
string filter = ...;
var filteredRights = context.Entry(userRole).Collection(e => e.Right).Query();
if (!string.IsNullOrWhiteSpace(filter))
{
    filteredRights = filteredRights.Where(e => e.RightName.Contains(filter));
}
var rights = filteredRights.ToList();
和SQL

SELECT 
    [Extent1].[RightName] AS [RightName]
    FROM [dbo].[Role_Right] AS [Extent1]
    WHERE ([Extent1].[RoleId] = @EntityKeyValue1) AND ([Extent1].[RightName] LIKE @p__linq__0 ESCAPE '~')
-- EntityKeyValue1: '42' (Type = Int32, IsNullable = false)
-- p__linq__0: '%~_a%' (Type = AnsiString, Size = 8000)

好。。这个问题与Linq表达式的延迟\立即加载有关,考虑到LinqToSql编译所支持的类型方法。您能否修复或澄清关于“这次我需要使用
IndexOf
,因为它使用
string.Contains
…”的行。。。除了行不清楚之外,这里还有一个误解,SQL
LIKE
操作既不区分大小写,也不区分大小写-列与表相关的排序规则将是阻止因素。在这里,您可以像在SQL、toLower或SQL中一样处理不区分大小写的检查toLowerVariant@BrettCaswell我澄清了这个句子。除此之外,我只处理过不区分大小写的数据库,所以我从来没有考虑过这种情况。好吧,我知道了主要区别是什么<代码>上下文。右侧的类型为
DbSet
,它是一个
IQueryable
,后续的扩展方法
Where
。但是,
userRole.Right
返回一个
ICollection
,它是一个
IEnumerable
,后面的
Where
也是如此。有没有办法使实体对象的relationship属性成为
IQueryable
AsQueryable
不起作用。Thx,我将尝试如果不使用
StringComparison
会发生什么。至于下划线,我伪造了一点数据,只是以
\u a
为例。SQL转换器做得很好。好的,所以我在我知道的地方尝试了
IndexOf
而没有
StringComparison