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

本文关键字:执行 新执行 查询 Linq 集合 对象 引用 延迟 | 更新日期: 2023-09-27 18:06:09

我目前正在尝试编写一些代码,将在两个独立的数据库上运行查询,并将结果返回给匿名对象。有了两个匿名对象集合之后,我需要对这两个集合执行比较。比较是,我需要检索webOrders中的所有记录,而不是foamOrders中的所有记录。目前,我正在使用Linq进行比较。我的主要问题是,两个原始查询都返回大约30,000条记录,而我现在的代码需要很长时间才能完成。我是使用Linq的新手,所以我试图理解使用Linq来比较两个匿名对象集合是否会导致数据库查询一遍又一遍地运行——由于延迟执行。这可能是一个显而易见的答案,但我对Linq和匿名对象如何与延迟执行一起工作还没有非常坚定的理解。我希望有人能给我点化一下。下面是我的代码…

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

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

当你这样做时,你的linq将不再被延迟

ToDataTable();

此时,它被快照为完成并永远清除。

当你转换它时,foamOrders和webOrders也是如此

ToList();

您可以将其作为一个查询。我没有mySQL来检查

关于延迟执行:

方法.ToList()遍历IEnumerable,获取所有值并用这些值填充一个新的List<T>对象。因此,此时肯定不会延迟执行。

这很可能与.ToDataTable();相同

注:但我建议:

  1. 使用自定义类型而不是匿名类型
  2. 不要使用LINQ来比较对象,因为它不是真正有效的(LINQ正在做额外的工作)
  3. 你可以创建一个自定义的MyComparer类(可能实现IComparer接口)和Compare<T1, T2>这样的方法来比较两个实体。然后,您可以创建另一个方法来比较两组实体,例如T1[] CompareRange<T1,T2>(T1[] entities1, T2[] entities2),它在循环中重用您的compare方法并返回操作
  4. 的结果

注:其他一些可能导致显著性能损失的资源密集型操作(如果需要执行数千个操作):

  1. 枚举对象(foreach循环或LINQ方法)的使用

可能的解决方案:如果可能的话,尝试使用for循环。

  • 大量使用匿名方法(编译器需要大量时间来编译lambda表达式/运算符);
  • 可能的解决方案:将lambdas存储在委托中(如Func<T1, T2>)

    为了将来对任何人有所帮助,我的新代码粘贴在下面。它现在运行快多了。多亏了大家的帮助,我了解到,即使数据库查询的延迟执行被切断,使用. tolist()后结果变成静态,但使用Linq比较结果集合的效率非常低。我用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; }
        }
    
     }