开发者

how to handle source data changes in LINQ?

i have a collection of items where each item has a "date" field (the code is below).

i am trying to fill in any gaps in dates in the collection using LINQ. in particular, i want the resulting sequence to contain all days between the first and the last day in the original sequence.

in addition to this, my resulting LINQ query should be able to handle any modifications of the original sequence. that is i cannot calculate the minimal and the maximal dates ahead of time.

so i tried the code below but it f开发者_StackOverflow社区ails when it tries to calculate Min and Max of the sequence. i am looking for a "lazy" alternative.

thanks for any help konstantin


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace consapp
{
    class C
    {
        public DateTime date;
        public int? value;
    }

    static class Program
    {
        static IEnumerable<C> dates(DateTime d0, DateTime d1)
        {
            for (var d = d0; d <= d1; d = d.AddDays(1))
            {
                yield return new C { date = d };
            }
        }

        static void Main(string[] args)
        {
            var xs = new ObservableCollection<C>();

            var q = from d in dates(xs.Min(y => y.date), xs.Max(y => y.date))
                    join x in xs on d.date equals x.date into js
                    from j in js.DefaultIfEmpty()
                    orderby d.date
                    select new { date = d.date, value = j != null ? j.value : null };

            xs.Add(new C { date = DateTime.Parse("11/10/11") });
            xs.Add(new C { date = DateTime.Parse("02/02/11") });
            xs.Add(new C { date = DateTime.Parse("11/24/11") });
            xs.Add(new C { date = DateTime.Parse("09/09/11") });
            xs.Add(new C { date = DateTime.Parse("11/10/11") });

            foreach (var x in q)
            {
                Console.WriteLine(x.date.ToShortDateString());
            }
        }
    }
}


I'm not absolutely positive, but:

var q = from d in dates(xs.Min(y => y.date), xs.Max(y => y.date))

I believe that the "dates" method will be called immediately, and the rest of the LINQ query (including the iterator from dates() itself) will be built up around the result from that method. So you are going to have to pre-populate xs with the data you are interested in.

This is because LINQ essentially works by wrapping enumerables in other enumerables. In order for it to do this, it must start with an enumerable. In order to do that, it must call your order() method, which requires supplying its arguments immediately, so that it can receive the enumerable object that it will be wrapping in other enumerables. So the xs.Min and xs.Max methods will be called when that line of code is reached, but nothing else in the query will actually be processed.

A workaround would be to have your dates() method actually receive the ObservableCollection and call Min/Max itself. Because this will happen in the generated iterator, execution of those calls will be deferred as you expect.


Standard LINQ implementation based on IEnumerable<T> cannot handle data sources that are changing such as ObservableCollection. The reason why your example fails is that it will try to evaluate the data source (and call the dates function and Min and Max operators) when you define the query (but the data source doesn't contain any data at that point).

  • One option is to use an alternative LINQ implementation that works with ObservableCollection and can automatically update the result when the source changes. As far as I know Bindable LINQ project should be able to do that.

  • Another (simpler) option is to turn your query into a method and call the method repeatedly (to update the result) when you know that the data source has changed. You'd have to make ObservableCollection a private field and the method would simply run using the data currently stored in the collection:

    private ObservableCollection source;
    void UpdateResults() { 
      var q = /* The query goes here */
      // Do something with the result of the query
    }
    
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜