Search code examples
c#wpfmvvmdata-bindingwpfdatagrid

Does WPF DataGrid Implicitly Convert IEnumerable<x> to List<x> When Binding?


Most of my question is in the title: Does WPF DataGrid Implicitly Convert IEnumerable<X> ItemsSource to List<x>?

I have a WPF, using MVVM, etc. ViewModel property is defined as ...

    private IEnumerable<Plan> _PlanListItems;
    public IEnumerable<Plan> PlanListItems
    {
        get { return _PlanListItems; }
        protected set { RaiseAndSetIfChanged(ref _PlanListItems, value); }
    }

    // ...
    // in some method
    OpenPlanItems = await service.GetAllPlansAsync();

In service ...

    public IEnumerable<Plan> GetAllPlans()
    {
        using (MyEntities ent = new MyEntities())
        {
            return (from a in ent.DbPlans select a)
                .AsEnumerable()
                .Select(o => o.Convert());   // Does some stuff to convert DbPlan entity into Plan class
        }
    }

    public async Task<IEnumerable<Plan>> GetAllPlansAsync()
    {
        return await Task.Run(() => GetAllPlans());
    }

In XAML ...

    <DataGrid ItemsSource="{Binding PlanListItems}"  ...>

This throws an exception: The operation cannot be completed because the DbContext has been disposed error. Another question indicates a cause could be due to late execution on the IEnumerable object (i.e. trying to enumerate after the context has been disposed).

Additionally, I have similar code that has a .ToList() conversion inside a method similar to GetAllPlans(). Something like this, but not the actual code:

    public List<Plan> GetAllPlans()
    {
        using (MyEntities ent = new MyEntities())
        {
            return (from a in ent.DbPlans select a)
                .AsEnumerable()
                .Select(o => o.Convert()).ToList();   // Does some stuff to convert ...
        }
    }

It turned out to be a slow method (>5 seconds). When I removed the .ToList() conversion, it dropped the time to <0.2 seconds, but the inefficiency returned (and almost the exact same duration) when I bind the result to a DataGrid.

This leads me to believe that WPF DataGrid implicitly converts IEnumerable bound items to a List as part of the binding process. Is that true? And if so, is there a way to avoid the implicit conversion?


Solution

  • The reason your IEnumerable<T> service takes < 0.2 seconds to return is because it isn't actually returning data: it is returning more of 'how to get the data, when you're actually interested in it'. It will only actually give you the data when you iterate over it or otherwise access it. This will give you the DbContext has been disposed of error you had, if the client code tries to make use of that 'how to get the data' outside of the scope of the context.

    This is why when you have .ToList() on your service it takes longer: because it is actually returning data. Real data you can really use on the client without worrying about the scope of the context.

    The DataGrid just needs its ItemsSource collection to implement the IEnumerable interface, so it can enumerate over the items as and when it needs to. It isn't "converting things to a list" per se, it is just enumerating the collection.