【问题标题】:How to do Entity Framework/Linq join in query with where clause expressions如何使用 where 子句表达式进行实体框架/Linq 加入查询
【发布时间】:2022-01-23 21:40:31
【问题描述】:

我有一个实体框架查询,其中 where 子句根据多个选择而变化。
这是我的代码:

Expression<Func<CtCsGroup, bool>> where_expression1 = x => 1 == 1;
Expression<Func<CtCsGroup, bool>> where_expression2 = x => 1 == 1;
Expression<Func<CtCsGroup, bool>> where_expression3 = x => 1 == 1;


if (selections.SearchCSGroup.NoNull() != "")
{
    where_expression1 = x => x.CsGroup.ToUpper() == selections.SearchCSGroup.ToUpper();
}

if (selections.SearchUserId.NoNull() != "")
{
    where_expression3 = x => x.UsrIdn.ToUpper() == selections.SearchUserId.ToUpper();
}

if (!selections.SearchIncludeRetires)
{
    where_expression2 = x => x.CsRetire != "Y";
}

var groupDTOs = (from g in _infobaseContext.CtCsGroup
                 .Where(where_expression1)
                 .Where(where_expression2)
                 .Where(where_expression3)
                 select new
                 {
                     UsrIdn = g.UsrIdn,
                     CsGroup = g.CsGroup,
                     CsRetire = g.CsRetire,
                     CsGroupName = "test" // n.CsGroupName
                 }).ToList();

我想向另一个表添加一个联接。

如何将其添加到上述查询中?我尝试了几种不同的格式,但每当我添加它时,我都会收到错误 "(where_expression1)""(where_expression3)" 等。

谢谢。

这里有更多细节......

var groupDTOs = (from g in _infobaseContext.CtCsGroup
                         join n in _infobaseContext.TblCsGroupList
                                    on g.CsGroup equals n.CsGroup
                         select new
                         {
                             UsrIdn = g.UsrIdn,
                             CsGroup = g.CsGroup,
                             CsRetire = g.CsRetire,
                             CsGroupName =  n.CsGroupName
                         }).ToList();

  • 而且这个没有连接也可以工作 -
var groupDTOs = (from g in _infobaseContext.CtCsGroup
                         .Where(where_expression1)
                         select new
                         {
                             UsrIdn = g.UsrIdn,
                             CsGroup = g.CsGroup,
                             CsRetire = g.CsRetire,
                             CsGroupName =  "test"  //n.CsGroupName
                         }).ToList();

  • 但是当我同时添加连接和表达式时 -
var groupDTOs = (from g in _infobaseContext.CtCsGroup
                         join n in _infobaseContext.TblCsGroupList
                                    on g.CsGroup equals n.CsGroup
                         .Where(where_expression1)
                         select new
                         {
                             UsrIdn = g.UsrIdn,
                             CsGroup = g.CsGroup,
                             CsRetire = g.CsRetire,
                             CsGroupName =  n.CsGroupName
                         }).ToList();

  • Visual Studio 出现此错误: 参数 2:不能从 'System.Linq.Expressions>' 转换为 'System.Func'

【问题讨论】:

  • 请使用标签来指示您正在使用的确切 EF 版本。如果你想加入,你应该使用join 语句或者更好的导航属性。但是您应该显示引发异常的代码和逐字异常消息。你的问题现在还不清楚。
  • 我的第一个反应是为什么你会表达这样的基于条件的WHERE 子句。为什么不在多个语句上编写查询?
  • Here are some more details... 下面的所有内容都无关紧要,尽管我已经在我的回答中对此做出了回应,但您应该包含的是 exact 错误消息和匹配的 where_expression创造了它。错误消息非常详细,尽管您必须学习如何解释它们。
  • 你有equals n.CsGroup.Where(where_expression1)。这不可能。您可能错过了一些括号。

标签: entity-framework linq join expression


【解决方案1】:

通常,我们会努力通过多个步骤组合查询,而不是尝试注入多个预先组合的谓词。您已经在使用 Fluent 表示法来应用谓词,因此将该过程从基于 Query 的表达式中分离出来。

您还会注意到比较值已被参数化,而不是让 linq 解释器决定什么是查询引用,什么是离散值或标量值。

您尚未发布具体错误,但是在评估 linq 表达式时,elections.SearchCSGroup 可能超出范围且无法解决。有意的参数化避免了这种混淆。

IQueryable<CtCsGroup> groupQuery = _infobaseContext.CtCsGroup;
if (selections.SearchCSGroup.NoNull() != "")
{
    // Force the comparison against a discrete parameter value
    var selection = selections.SearchCSGroup.ToUpper();
    groupQuery = groupQuery.Where(x => x.CsGroup.ToUpper() == selection);
}

if (selections.SearchUserId.NoNull() != "")
{
    var selection = selections.SearchUserId.ToUpper();
    groupQuery = groupQuery.Where(x => x.UsrIdn.ToUpper() == selection);
}

if (!selections.SearchIncludeRetires)
{
    // NOTE: "Y" will be correctly interpreted as a discrete parameter value
    groupQuery = groupQuery.Where(x => x.CsRetire != "Y");
}

var groupDTOs = (from g in groupQuery 
                 select new
                 {
                     UsrIdn = g.UsrIdn,
                     CsGroup = g.CsGroup,
                     CsRetire = g.CsRetire,
                     CsGroupName = "test" // n.CsGroupName
                 }).ToList();

最后一部分也可以组成一个 fluent 表达式,而不是一个基于 query 的表达式:

var groupDTOs = groupQuery.Select(g => new
                {
                    UsrIdn = g.UsrIdn,
                    CsGroup = g.CsGroup,
                    CsRetire = g.CsRetire,
                    CsGroupName = "test" // n.CsGroupName
                }).ToList();

此模式通过首先尝试通过预编译谓词来帮助您避免这些问题,尤其是它消除了使用不正确的类型绑定定义谓词的可能性。

此模式还有助于编写具有条件排序列或方向的表达式。


你发布的最后一个问题是红鲱鱼,你把语法弄混了,那个表达本来就没有用。

Visual Studio 出现此错误:参数 2:无法从 'System.Linq.Expressions>' 转换为 'System.Func'

特别是本节:

join n in _infobaseContext.TblCsGroupList
          on g.CsGroup equals n.CsGroup.Where(where_expression1)

您现在可能会看到,换行符已删除,n.CsGroup 是一个字符串,它本身是IEnumerable&lt;char&gt;,因此假设您要将where_expression1 谓词应用于n.CsGroup 的值

我们仍然可以使用普通的 linq 连接表达式连接到组合查询:

var groupDTOs = (from g in groupQuery
                 join n in _infobaseContext.TblCsGroupList
                        on g.CsGroup equals n.CsGroup 
                 select new
                 {
                     UsrIdn = g.UsrIdn,
                     CsGroup = g.CsGroup,
                     CsRetire = g.CsRetire,
                     CsGroupName = n.CsGroupName
                 }).ToList();

如果您的架构在CtCsGroup 上有一个名为CsGroup 的导航属性,它表示TblCsGroupListCtCsGroup 之间的关系,那么我们可以使用点符号 和必要的连接 将是隐含的。

注意:这对架构做出了广泛的假设,并且仅用于显示手动定义连接的常见结构替代方案

var groupDTOs = (from g in groupQuery
                 select new
                 {
                     UsrIdn = g.UsrIdn,
                     CsGroup = g.CsGroup,
                     CsRetire = g.CsRetire,
                     CsGroupName = g.CsGroup.CsGroupName
                 }).ToList();

【讨论】:

  • 感谢您的帮助。我在上面使用了您的代码,效果很好,但我仍然需要添加一个简单的连接。我想在 CsGroup = CsGroup 上对 TblCsGroupList 进行外部连接以获取 TblCsGroupList.CsGroupName 。我已经看到了如何做到这一点的不同例子,但我不确定最好/最简单的方法是什么。你有什么推荐?
  • CsGroupTblCsGroupList 之间是否存在导航属性?如果您的数据结构具有通过外键定义的导航属性,则不需要连接,因此要回答最好的方法是需要有关您的架构的更多信息,但是我已经用一个简单的连接示例更新了帖子
  • 效果很好。谢谢。仅供参考 - 我正在处理没有外键的旧数据,因此没有使用导航属性和选项。
  • @DanK。不要让遗留问题阻止您;) 您可以将 FK 添加到业务模型中,即使它们不在数据库中,也可以将 Navigation Property 视为预定义的连接。它们极大地简化了整个应用中的常见加入逻辑,并指导开发人员做出正确的决定
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 1970-01-01
  • 2011-10-21
  • 2020-07-19
相关资源
最近更新 更多