C# 如何提高我的LINQ

C# 如何提高我的LINQ,c#,linq,C#,Linq,下面的查询确实解决了我的问题,但它似乎过于复杂 问题是我有一个存储菜单选项(id、名称、说明和价格)的表。我还有一个会话变量,它将用户的选择存储在字典中(存储id和数量) 我在下面生成的LINQ基本上计算了所有汇总的值。它将表格转换为字典,然后将价格乘以数量求和 有更好的方法吗 var prices = _db.MenuOptions .Select(o => new { o.Id, o.Price }) .ToDiction

下面的查询确实解决了我的问题,但它似乎过于复杂

问题是我有一个存储菜单选项(id、名称、说明和价格)的表。我还有一个会话变量,它将用户的选择存储在字典中(存储id和数量)

我在下面生成的LINQ基本上计算了所有汇总的值。它将表格转换为字典,然后将价格乘以数量求和

有更好的方法吗

var prices = _db.MenuOptions
                .Select(o => new { o.Id, o.Price })
                .ToDictionary(o => o.Id, o => o.Price);
Session["price"] = prices
              .Where(p => orderItems.Keys.Contains((int)p.Key))
              .Sum(p => p.Value * orderItems[p.Key]);

我马上想到的一件事是循环使用
orderItems.Keys
,而不是
prices
,然后按键过滤它们

假设orderItems是一个标准字典,那么它实现了
IEnumerable
。这意味着我们可以做如下事情:

Session["price"] = orderItems.Sum(x=>x.Value*prices[x.Key]);
您可能可以通过执行联接将所有内容合并到一个查询中,但我个人认为,这种方式更具可读性

除了更简洁、可读性更强之外,这样做更好的原因是,当原始方法只需要非常有限数量的元素时,它将遍历所有的price字典。实际上,只有与orderItems匹配的元素。因此,当我们知道我们想要所有的物品时,在较小的清单上订购更有意义


只有当orderItems字典中的某个项目在prices字典中没有相应的条目时,这才有可能失败,但我认为这永远不会发生。如果是这样,您需要进行适当的防护。

选择的
部分没有必要,可以删除:

var prices = _db.MenuOptions.ToDictionary(o => o.Id, o => o.Price);
价格
计算可以从
订单项
开始:

Session["price"] = orderItems.Sum(oi => oi.Value * prices[oi.Key]);
(假设所有
orderItems
都有db中的价格。)

编辑:从Arcturus answer开始,对于“一行”也可以这样做,但速度可能较慢(见注释):


既然可以直接使用数据,为什么要先创建一个匿名类,并将这些内容放在字典中呢

Session["price"] = _db.MenuOptions
               .Where(p => orderItems.Keys.Contains((int)p.Id))
               .Sum(p => p.Price * orderItems[p.Id]);

实际上,原始代码并不坏,因为它只加载所需的列,尽管它对所有菜单选项都这样做

更好的解决方案是仅从数据库加载订购项目的价格:

var prices = _db.MenuOptions
                .Where(o => orderItems.Keys.Contains(o.Id))
                .Select(o => new { o.Id, o.Price })
                .ToDictionary(o => o.Id, o => o.Price);

Session["price"] = orderItems.Sum(oi => oi.Value * prices[oi.Key]);
Select
语句确保只加载ID和Price,并且不能删除。

Where
语句将在(..)
子句中创建一个
Where ID,只返回订单项目的价格。

我觉得还可以,我在这里看到了更多奇特的结构。
\u db
是什么?您使用的是类似ORM的实体框架还是NHibernate?\u db使用的是EntityFramework。这个问题似乎与主题无关,因为它是关于改进工作代码,而不是解决实际问题。我想这会很好,我会在以后的评论中发表。我的问题似乎已经在这里得到了回答。谢谢。如果orderItems包含的项目可能比价格多,这可能不起作用。@Arcturus:我刚刚添加了一条关于这一点的注释。从上下文来看,我认为这是不应该发生的(我认为这是一种电子商务类型的情况,订单项是从所有有价格的产品中选择的),但这肯定是值得提高的。非常正确。。奇怪的是,这个示例正在解析菜单项中的价格,而不是使用数据库值。@Arcturus:的确如此。我不想开始怀疑这一点。:)这里的菜单选项可能意味着在餐厅用餐或类似的事情。这可能无法编译,具体取决于
\u db
是什么。如果它是像EF这样的ORM,它可能会抱怨它不能将
orderItems[p.Id]
转换为SQL对不起,我应该提到它使用的是EntityFramework,因此我首先将数据拉入业务逻辑。比我的要简洁得多,另外还简化了第一部分(我很不好意思错过了那个简单的优化)。一行程序将循环遍历每个订单项的所有选项。它与原始的不一样,性能更差。如果
\u db
是ORM,您甚至会执行多个queries@PanagiotisKanavos澄清了这一点。另一个问题是,您正在加载所有选项的所有字段,即使只有已排序的ite的选项ms是必需的。
select
部分只需加载价格和IDs@PanagiotisKanavos你是说只有一行吗?我认为LINQ比这更聪明,但可能不是……在这种情况下,
prices
orderItems
应该具有相同的长度,从何处开始定价并不重要因此,为了使原始代码的变化最小,只需移动
Where()
方法即可。
var prices = _db.MenuOptions
                .Where(o => orderItems.Keys.Contains(o.Id))
                .Select(o => new { o.Id, o.Price })
                .ToDictionary(o => o.Id, o => o.Price);

Session["price"] = orderItems.Sum(oi => oi.Value * prices[oi.Key]);