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 }
精彩评论