简体   繁体   English

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

[英]Linq - Using a query as function in a select

When I'm calling a function in a select, I get this Error: A second operation started on this context before a previous operation completed. 当我在一个select中调用一个函数时,出现此错误:在上一个操作完成之前,在此上下文中启动了第二个操作。

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();
    }

Can I do it like this or do I have to call func2 in a foreach function? 我可以这样做吗,还是必须在foreach函数中调用func2? (Already tested it and it works with a foreach). (已经对其进行了测试,并且可以与foreach一起使用)。

You have to realize that your query implements IQueryable<...> . 您必须意识到您的查询实现了IQueryable<...>

An object that implements IQueryable has an Expression and a Provider . 实现IQueryable的对象具有一个Expression和一个Provider The Expression is a generic representation of what must be queried. Expression是必须查询的内容的通用表示形式。 The Provider knows who must execute the query and what language this executor uses. Provider知道谁必须执行查询以及该执行者使用哪种语言。

When you start enumerating the sequence that the IQueryable represents (= when you call .ToList() ) , the Expression is sent to the Provider . 当开始枚举IQueryable表示的序列时(=调用.ToList() ),该Expression将发送到Provider The Provider will translate the Expression into the language that the process that must execute the query understands (usually SQL) and sent it to the executing process. Provider会将Expression转换为必须执行查询的过程所理解的语言(通常是SQL),并将其发送给执行过程。

The returned data is put into an object that implements IEnumerable<...> , and this object is enumerated. 返回的数据放入实现IEnumerable<...>对象中,并枚举此对象。

The problem is, that the provider only knows how to translate fairly basic Expressions into SQL. 问题在于,提供程序仅知道如何将相当基本的Expressions转换为SQL。 It does not know how to translate Func2 . 它不知道如何翻译Func2

I don't see that you use any T3 item in your query. 我看不到您在查询中使用任何T3项目。 Is that a typing error? 那是打字错误吗?

Anyway, the easiest solution would be to put the code of Func2 into Func1: 无论如何,最简单的解决方案是将Func2的代码放入Func1:

(I'm more familiar with LINQ method syntax, than LINQ query syntax, but you'll get the gist) (与LINQ查询语法相比,我对LINQ方法语法更熟悉,但是您将了解要点)

see Enumerable.Join 参见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();
    });

If you'll use Func2 often in various different Expressions, you could consider to transform Func2 such that it takes an IQueryable as input. 如果您经常在各种不同的表达式中使用Func2,则可以考虑对Func2进行转换,使其将IQueryable作为输入。

I created it as an extension function of IQueryable<Table2Row> . 我将其创建为IQueryable<Table2Row>的扩展函数。 See 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();
}

Now you can use it in the ResultSelector of the Join: 现在,您可以在联接的ResultSelector中使用它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM