【问题标题】:Selecting changes in values with Lambda, Linq, Entity Framework使用 Lambda、Linq、Entity Framework 选择值的更改
【发布时间】:2013-06-25 11:53:42
【问题描述】:

我有以下实体框架类定义:

class TimeValue
{
    DateTime StartDate;
    double Value;
}

假设给定以下一系列值,我只想知道值何时发生变化:

2000-01-01  100
2000-01-15  100
2000-02-01  110
2000-02-15  120
2000-03-01  120
2000-03-15  50
2000-04-01  50
2000-04-15  50
2000-05-01  120

所以结果是:

2000-01-01  100
2000-02-01  110
2000-02-15  120
2000-03-15  50
2000-05-01  120

我可以使用 lambda/linq 很好地选择值。然后我使用以下代码遍历结果以添加到列表中:

var timeValueQuery = _context.TimeValues.Where(...);

List<TimeValue> timeChanges = new List<TimeValue>();
TimeValue lastValue = null;
foreach (var tvq in timeValueQuery)
{
    if (lastValue == null || tvq.Value != lastValue.Value)
    {
        timeChanges.Add(tvq);
    }
    lastValue = tvq;
}

只是想知道是否有更快/更好的方法。

【问题讨论】:

    标签: c# linq lambda


    【解决方案1】:

    您可以创建仅在满足某些条件时才返回项目的扩展方法。这个条件(谓词)将接受两个参数——前一项和当前项,它应该比较:

    public static IEnumerable<T> TakeIf<T>(this IEnumerable<T> source, 
                                           Func<T, T, bool> predicate)
    {
        var enumerator = source.GetEnumerator();
        if (!enumerator.MoveNext())
            yield break;
    
        yield return enumerator.Current;
        T previous = enumerator.Current;
    
        while (enumerator.MoveNext())
        {
            T current = enumerator.Current;
            if (predicate(previous, current))
                yield return current;
    
            previous = current;
        }
    }
    

    通过谓词检查先前和当前项目是否具有不同的值。用法:

    var timeChanges = _context.TimeValues.TakeIf((x, y) => x.Value != y.Value);
    

    【讨论】:

    • @Andez 谢谢 :) 顺便说一句,我还要添加检查源是否为 null 以避免 NullReferenceException
    • 聪明。只需确保数据是有序的。
    【解决方案2】:

    再次阅读您的数据集后进行编辑。

    我发现分组不起作用。理想情况下,由于网络 io 等原因,您希望在 db 服务器上而不是客户端上使用此逻辑。

    您可以将其编写为 proc 或动态 sql。短期内动态 sql 可能会更容易。

    简单的方法,选择它变成一个光标。循环进入临时表变量并返回结果集。

    如果您的数据集非常小,那么请坚持使用您拥有的易于阅读的 linq 循环。除非您也需要,否则不要增加更多复杂性。即不要进行微优化。

    【讨论】:

    • 那行不通 - 如果它两次更改为相同的值,它只会给你最后一个。
    【解决方案3】:

    不知道这是否更好:) 反正:

      IQueryable<TimeValue> values = ...; // initialize here
      var query = from v in values
                  where (from v2 in values
                         orderby v2.StartDate
                         where v2.StartDate > v.StartDate
                         select v2.Value).FirstOrDefault() != v.Value
                  select v;
    

    如果可能有零值,请将 FirstOrDefault() 更改为 FirstOrDefault(impossible_value)。同样 for 循环可能更有效(但您要求使用 LINQ)。

    【讨论】:

      猜你喜欢
      • 2014-02-18
      • 1970-01-01
      • 1970-01-01
      • 2011-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-14
      • 1970-01-01
      相关资源
      最近更新 更多