简体   繁体   English

了解延迟执行:是否重新执行Linq查询每次引用其匿名对象集合时?

[英]Understanding Deferred Execution: Is a Linq Query Re-executed Everytime its collection of anonymous objects is referred to?

I'm currently trying to write some code that will run a query on two separate databases, and will return the results to an anonymous object. 我目前正在尝试编写一些代码,这些代码将在两个单独的数据库上运行查询,并将结果返回给匿名对象。 Once I have the two collections of anonymous objects, I need to perform a comparison on the two collections. 一旦我有两个匿名对象集合,我需要对两个集合进行比较。 The comparison is that I need to retrieve all of the records that are in webOrders, but not in foamOrders. 比较是我需要检索webOrders中的所有记录,但不需要检索foamOrders中的所有记录。 Currently, I'm making the comparison by use of Linq. 目前,我正在使用Linq进行比较。 My major problem is that both of the original queries return about 30,000 records, and as my code is now, it takes waay too long to complete. 我的主要问题是两个原始查询都返回了大约30,000条记录,而现在我的代码需要花费太长时间才能完成。 I'm new to using Linq, so I'm trying to understand if using Linq to compare the two collections of anonymous objects will actually cause the database queries to run over and over again - due to deferred execution. 我是新手使用Linq,所以我试图理解是否使用Linq比较两个匿名对象集合实际上会导致数据库查询反复运行 - 由于延迟执行。 This may be an obvious answer, but I don't yet have a very firm understanding of how Linq and anonymous objects work with deferred execution. 这可能是一个明显的答案,但我还没有非常明确地理解Linq和匿名对象如何使用延迟执行。 I'm hoping someone may be able to enlighten me. 我希望有人能够启发我。 Below is the code that I have... 以下是我的代码......

private DataTable GetData()
{
    using (var foam = Databases.Foam(false))
    {
        using (MySqlConnection web = new MySqlConnection(Databases.ConnectionStrings.Web(true)
        {
            var foamOrders = foam.DataTableEnumerable(@"
                    SELECT order_id
                    FROM   Orders
                    WHERE  order_id NOT LIKE 'R35%'
                    AND originpartner_code = 'VN000011'
                    AND orderDate > Getdate() - 7 ")
                .Select(o => new
                {
                    order = o[0].ToString().Trim()
                }).ToList();

            var webOrders = web.DataTableEnumerable(@"
                    SELECT ORDER_NUMBER FROM TRANSACTIONS AS T WHERE
                    (Str_to_date(T.ORDER_DATE, '%Y%m%d %k:%i:%s') >= DATE_SUB(Now(),  INTERVAL 7 DAY))
                    AND (STR_TO_DATE(T.ORDER_DATE, '%Y%m%d %k:%i:%s') <= DATE_SUB(NOW(),  INTERVAL 1 HOUR))")
                .Select(o => new
                {
                    order = o[0].ToString().Trim()
                }).ToList();
            return (from w in webOrders
                    where !(from f in foamOrders
                            select f.order).Contains(w.order)
                    select w
                ).ToDataTable();
        }
    }
}

Your linq ceases to be deferred when you do 当你这样做时,你的linq不再被推迟

ToDataTable();

At that point it is snapshotted as done and dusted forever. 在那时,它被拍摄为已完成并永远拂去灰尘。

Same is true with foamOrders and webOrders when you convert it 转换它时,foamOrders和webOrders也是如此

ToList();

You could do it as one query. 你可以把它做成一个查询。 I dont have mySQL to check it out on. 我没有mySQL来检查它。

Regarding deferred execution: 关于延期执行:

Method .ToList() iterates over the IEnumerable retrieves all values and fill a new List<T> object with that values. 方法.ToList()迭代IEnumerable检索所有值并用该值填充新的List<T>对象。 So it's definitely not deferred execution at this point. 所以在这一点上绝对不是延迟执行。

It's most likely the same with .ToDataTable(); 它最有可能与.ToDataTable();相同.ToDataTable();

PS But i'd recommend to : PS但我建议:

  1. Use custom types rather than anonymous types. 使用自定义类型而不是匿名类型。
  2. Do not use LINQ to compare objects because it's not really effective (linq is doing extra job) 不要使用LINQ比较对象,因为它不是真正有效(linq正在做额外的工作)
  3. You can create a custom MyComparer class (that might implement IComparer interface) and method like Compare<T1, T2> that compares two entities. 您可以创建自定义MyComparer类(可能实现IComparer接口)和比较两个实体的Compare<T1, T2>类的方法。 Then you can create another method to compare two sets of entities for example T1[] CompareRange<T1,T2>(T1[] entities1, T2[] entities2) that reuse your Compare method in a loop and returns result of the operation 然后你可以创建另一个方法来比较两组实体,例如T1[] CompareRange<T1,T2>(T1[] entities1, T2[] entities2)在循环中重用你的Compare方法并返回操作的结果

PS Some of other resource-intensive operations that may potentially lead to significant performance losses (in case if you need to perform thousands of operations) : PS一些其他资源密集型操作可能会导致重大性能损失(如果您需要执行数千次操作):

  1. Usage of enumerator object ( foreach loop or some of LINQ methods) 枚举器对象的使用( foreach循环或一些LINQ方法)

Possible solution : Try to use for loop if it is possible. 可能的解决方案:如果可能,尝试使用for循环。

  1. Extensive use of anonymous methods (compiler requires significant time to compile the lambda expression / operator ); 广泛使用匿名方法(编译器需要大量时间来编译lambda表达式/运算符);

Possible solution : Store lambdas in delegates (like Func<T1, T2> ) 可能的解决方案:将lambda存储在委托中(如Func<T1, T2>

In case it helps anyone in the future, my new code is pasted below. 如果它将来帮助任何人,我的新代码将粘贴在下面。 It runs much faster now. 现在快得多运行。 Thanks to everyone's help, I've learned that even though the deferred execution of my database queries was cut off and the results became static once I used .ToList(), using Linq to compare the resulting collections was very inefficient. 感谢大家的帮助,我了解到即使我的数据库查询的延迟执行被切断,结果在我使用.ToList()后变为静态,使用Linq来比较结果集合的效率非常低。 I went with a for loop instead. 我转而使用for循环。

private DataTable GetData()
    {
        //Needed to have both connections open in order to preserve the scope of var foamOrders and var webOrders, which are both needed in order to perform the comparison.
        using (var foam = Databases.Foam(isDebug))
        {
            using (MySqlConnection web = new MySqlConnection(Databases.ConnectionStrings.Web(isDebug)))
            {
                var foamOrders = foam.DataTableEnumerable(@"
            SELECT foreignID
            FROM   Orders
            WHERE  order_id NOT LIKE 'R35%'
            AND originpartner_code = 'VN000011'
            AND orderDate > Getdate() - 7 ")
                                     .Select(o => new
                                     {
                                         order = o[0].ToString()
                                                     .Trim()
                                     }).ToList();


                var webOrders = web.DataTableEnumerable(@"
            SELECT ORDER_NUMBER FROM transactions AS T WHERE
                                                (Str_to_date(T.ORDER_DATE, '%Y%m%d %k:%i:%s') >= DATE_SUB(Now(),  INTERVAL 7 DAY))
                                                AND (STR_TO_DATE(T.ORDER_DATE, '%Y%m%d %k:%i:%s') <= DATE_SUB(NOW(),  INTERVAL 1 HOUR))
                                                 ", 300)
                            .Select(o => new
                            {
                                order = o[0].ToString()
                                            .Trim()

                            }).ToList();


                List<OrderNumber> on = new List<OrderNumber>();

                foreach (var w in webOrders)
                {
                    if (!foamOrders.Contains(w))
                    {
                        OrderNumber o = new OrderNumber();
                        o.orderNumber = w.order;
                        on.Add(o);
                    }
                }

                return on.ToDataTable();

            }
        }


    }


    public class OrderNumber
    {
        public string orderNumber { get; set; }
    }


 }

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

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