Linq-在选择中将查询用作函数

时间:2019-02-15 11:25:09

标签: c# linq subquery entity-framework-core

在选择中调用函数时,出现此错误:在上一个操作完成之前,已在此上下文上启动了第二个操作。

public bool func2(int ID)
    {
        return (from t2 in _odc.table2
                where t2.ID == ID &&
                (t2.col1 > 0 ||
                t2.col2 != "" ||
                t2.col3 != "")
                select t2
               ).Any();
    }

public List<MyModel> func1()
    {

        return (from t1 in _odc.t1
                join t3 in _odc.t3 on t1.ID equals t3.ID
                where t1.col2 > 300
                where t1.col3 != 1
                where t1.col4 != 285
                where t1.col5 != 830
                where t1.col6 > 0
         select new MyModel
                {
                    ID = t1.ID,
                    isFunc2 = func2(t1.ID),
                }).ToList();
    }

我可以这样做吗,还是必须在foreach函数中调用func2? (已经对其进行了测试,并且可以与foreach一起使用。)

1 个答案:

答案 0 :(得分:0)

您必须意识到您的查询实现了IQueryable<...>

实现IQueryable的对象有一个Expression和一个ProviderExpression是必须查询内容的通用表示。 Provider知道谁必须执行查询以及该执行者使用哪种语言。

当您开始枚举IQueryable表示的序列时(=调用.ToList()时),Expression被发送到ProviderProvider会将Expression转换为必须执行查询的过程所能理解的语言(通常是SQL)并将其发送给执行过程。

返回的数据放入实现IEnumerable<...>的对象中,并枚举该对象。

问题在于,提供程序仅知道如何将相当基本的Expressions转换为SQL。它不知道如何翻译Func2

  

我没有看到您在查询中使用任何T3项目。那是打字错误吗?

无论如何,最简单的解决方案是将Func2的代码放入Func1:

(与LINQ查询语法相比,我对LINQ方法语法更熟悉,但是您将了解要点)

请参阅Enumerable.Join

var result = dbContext.T1
    // don't take all T1 items, but only the ones where:
    .Where(t1 => t1.col2 > 300
              && t1.col3 != 1
              && t1.col4 != 285
              && t1.col5 != 830
              && t1.col6 > 0
    // join the remaining t1s with table T3
    .Join dbContext.T3,
          t1 => t1.Id,               // from every T1 take the id
          t3 => t3.Id,               // from every T3 take the id

          // ResultSelector: take a t1 and a matching t3 to create one new object
          (t1, t3) => new MyModel
          {
              Id = t1.Id,

              // IsFunc2: true if Table2 has any element with Id equal to t1.Id and col values
              IsFunc2 = dbContext.Table2
                        .Where(t2 => t2.ID == t1.Id
                                  && (t2.col1 > 0 || t2.col2 != "" || t2.col3 != ""))
                        .Any();
    });

如果经常在各种不同的Expressions中使用Func2,则可以考虑对Func2进行转换,使其以IQueryable作为输入。

我将其创建为IQueryable<Table2Row>的扩展功能。参见extension methods demystified

static bool Func2(this IQueryable<Table2Row> rows, int id)
{
    return rows
        .Where(row => row.ID == id && (row.col1 > 0 || row.col2 != "" || row.col3 != ""))
        .Any();
}

现在,您可以在联接的ResultSelector中使用它:

(t1, t3) => new MyModel
{
    Id = t1.Id,
    IsFunc2 = dbContext.Table2.Func2(t1.Id),
});