C# EntityFramework:SQL上的Linq:Contains还是IndexOf?
我对EntityFramework6和.NET4.5(C#)有一种奇怪的情况 我在两个不同的地方提出了(几乎)相同的问题。但一次它查询数据库,第二次它查询内存中的对象。由于我在筛选子字符串,这是一个关键的区别: 数据库结构为表角色,右侧和跨表角色 第一次,我想查找尚未分配给角色的所有可用权限,并添加一个手动过滤器(这就是它变得复杂的原因),以减少结果列表: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.
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
…”的行。。。除了行不清楚之外,这里还有一个误解,SQLLIKE
操作既不区分大小写,也不区分大小写-列与表相关的排序规则将是阻止因素。在这里,您可以像在SQL、toLower或SQL中一样处理不区分大小写的检查toLowerVariant@BrettCaswell我澄清了这个句子。除此之外,我只处理过不区分大小写的数据库,所以我从来没有考虑过这种情况。好吧,我知道了主要区别是什么<代码>上下文。右侧的类型为DbSet
,它是一个IQueryable
,后续的扩展方法Where
。但是,userRole.Right
返回一个ICollection
,它是一个IEnumerable
,后面的Where
也是如此。有没有办法使实体对象的relationship属性成为IQueryable
AsQueryable
不起作用。Thx,我将尝试如果不使用StringComparison
会发生什么。至于下划线,我伪造了一点数据,只是以\u a
为例。SQL转换器做得很好。好的,所以我在我知道的地方尝试了IndexOf
而没有StringComparison