C# 使用Linq的条件where子句的计算和风格优化解
我第一次将Linq用于我的新MVC项目。 直到现在我都没有问题,但现在我被卡住了 我需要用条件where从句进行新的询问。 在网上搜索我找到了一些解决方案,几乎所有的解决方案都是这样的: 但是,我认为它的计算效率不高。 事实上,例如,如果我有一个表USERS和两个不同的过滤器NAME和PASSWORD,则执行如下操作:C# 使用Linq的条件where子句的计算和风格优化解,c#,linq,C#,Linq,我第一次将Linq用于我的新MVC项目。 直到现在我都没有问题,但现在我被卡住了 我需要用条件where从句进行新的询问。 在网上搜索我找到了一些解决方案,几乎所有的解决方案都是这样的: 但是,我认为它的计算效率不高。 事实上,例如,如果我有一个表USERS和两个不同的过滤器NAME和PASSWORD,则执行如下操作: var usr = context.USERS.Select(u => u).ToList(); var usr = context.USERS.Select(u =&g
var usr = context.USERS.Select(u => u).ToList();
var usr = context.USERS.Select(u => u)
.Where(u =>
((!string.IsNullOrWhiteSpace(name)) ? u.NAME == name : true)
&&
((!string.IsNullOrWhiteSpace(password)) ? u.PASSWORD == password : true))
.ToList();
提取所有表数据,然后过滤结果:
if (!string.IsNullOrWhiteSpace(name))
usr = usr.Where(u => u.NAME == name);
if (!string.IsNullOrWhiteSpace(password))
usr = usr.Where(u => u.PASSWORD == password);
这项工作正常的唯一方法是,框架在单个SQL命令中转换这项内容。
但通过调试,我们似乎首先获得了一个列表,然后在另一个步骤中过滤该列表
所以我想这样做:
var usr = context.USERS.Select(u => u).ToList();
var usr = context.USERS.Select(u => u)
.Where(u =>
((!string.IsNullOrWhiteSpace(name)) ? u.NAME == name : true)
&&
((!string.IsNullOrWhiteSpace(password)) ? u.PASSWORD == password : true))
.ToList();
现在,它似乎在一次射击中完成了所有任务
我想知道我的想法是否正确,第二种解决方案是否真的更好,是否有更好的解决方案
如果需要更新特定行,则在尝试更新表时也会出现类似的问题。
我会尽量解释得更好。。。
在前面的示例中,如果我需要更新由ID标识的特定用户行,我会执行以下操作:
var user = context.USERS.Where(u => (u.ID == 1)).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(name))
user.NAME == name;
if (!string.IsNullOrWhiteSpace(password))
user.PASSWORD == password;
context.SaveChanges();
即使在这种情况下,执行也分为两个步骤。如果您只是使用
var usr = context.USERS
或
然后usr是IQueryable的,在.ToList()调用(或类似调用)之前不会计算/执行
因此,要回答您的问题:
- 你的想法是正确的
- 第二种解决方案更好
- 至于更好的解决方案,我不确定有多少改进可以做,因为Linq将“优化”任何查询。就我个人而言,我更喜欢将.Where()子句分开,因为我觉得它更容易阅读
var usr = context.USERS.Select(u => u);
if (!string.IsNullOrWhiteSpace(name))
usr = usr.Where(u => u.NAME == name);
if (!string.IsNullOrWhiteSpace(password))
usr = usr.Where(u => u.PASSWORD == password);
return usr.ToList();
使用ToList()或FirstOrDefault()将从上下文加载实体。这意味着,在调用这些扩展之前,您可以在不查询数据库的情况下继续使用实例化的iQuery文件。您甚至可以传递它们并在其他方法中使用它们(只是要小心不要处理您的上下文或加载已经在其他上下文中加载的实体)
做你要做的事的正确方法是
var result = context.USERS.Where(u =>
(!string.IsNullOrWhiteSpace(name)|| u.NAME == name)
&&
(!string.IsNullOrWhiteSpace(password)|| u.PASSWORD == password));
另外,@James Sinclair回答的方式更具可读性,我建议您尽可能多地使用这种方式编写代码,因为它还允许将代码移动到另一种方法中,并添加任意数量的过滤器
如果过滤器数量未知,也可以使用表达式树,虽然通常速度较慢,但它提供了一种查询数据库的良好动态方式,而不会破坏LINQtoSQL的一般安全性
请注意,重用代码并能够理解操作的复杂性是以这种方式编写“performant”代码的基础(它仍然比编写表达式更重)
无论如何,作为一般规则,请记住,所有LINQ to SQL方法只会转换为SQL基本方法(因此没有带参数的构造函数或其他奇怪的东西)因为正在执行查询的是.ToList(),所以可以删除.ToList并在if之后调用它,这两种解决方案都是兼容的,只需执行
var usr=context.USERS
,您将只得到一个查询。如果需要枚举结果,您可以在末尾添加类似于return usr.ToList()
的内容。首先,谢谢您,您和@Sebastiano Roncato都非常清楚。我已经用一个新的部分修改了这个问题,我希望你能在这方面帮助我。我不知道是否有一个很好的方法可以直接使用linq进行更新。我一直在做与原始问题中的示例几乎相同的事情。从数据库(或任何地方)检索我需要的记录,在内存中编辑它,然后在context.SaveChanges()中将其推回到存储。我也不认为有更好的方法,因为实体框架只会在从数据库中获取实体时才开始跟踪该实体。我进行了一些搜索,看起来您只能将一个属性设置为已修改,然后将更改传播到数据库。您只需要创建一个具有相同tipe的实体,适当地设置键,将其附加到上下文中,并将属性设置为已修改。请看这里: