How can I change an Entity Framework ICollection to be an ObservableCollection?

So I'm pretty far down the rabbit hole of using the Entity Framework designer to make an EDMX that serves as the model in an MVVM project. I've just come across an issue where I'm pretty sure that the ICollection<> that was code generated (see below for example) really needs to be an ObservableCollection<> for binding that collection to a DataGrid in a view to be successful. I think I'm getting some hits on the possibility of modifying the EF code generation to make ObservableCollections rather than ICollections. Any one ever tried that successfully?

I suppose another option would be have the VM that contains the selected Customer object also contain a local ObservableCollection<Order> that gets created when the Customer object is selected....I just worry about the context saves and keeping the data in sync.

typical code gen object with an association to a collection of child objects :

    public partial class Customer
{
    public Customer()
    {
        this.Orders = new HashSet<Order>();
    }

    public int Id { get; set; }
    public System.DateTime Date { get; set; }

    public virtual ICollection<Order> Orders { get; set; }
}

That's what I did and what works for me with EF Database first.

That's what you need to be generated:

public partial class Parent
{
    public Parent()
    {
        this.Children= new ObservableCollection<Child>();
    }

So that default costructor will be replaced. And ObservableCollection is ICollection, so you don't need to change anything else.

To make this appear every time you update your database model you have to change your .tt file with following parts:

public string UsingDirectives(bool inHeader, bool includeCollections = true)
{
    return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
        ? string.Format(
            CultureInfo.InvariantCulture,
            "{0}using System;{1}" +
            "{2}",
            inHeader ? Environment.NewLine : "",
            includeCollections ? (Environment.NewLine + "using System.Collections.ObjectModel;" 
                + Environment.NewLine + "using System.Collections.Generic;") : "",
            inHeader ? "" : Environment.NewLine)
        : "";
}

and this:

    foreach (var navigationProperty in collectionNavigationProperties)
    {

    this.<#=code.Escape(navigationProperty)#> = new ObservableCollection<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();
    }

Your data logic and models should be separate from your viewmodel and models. So, I would think that the better option is what you are talking about in creating an ObservableCollection. You can always sync to the context (I forget the exact syntax to sync) when you save.


The DbSet class that is normally exposed via your DbContext has a Local property which is an ObservableCollection<T>. See the official documentation for more information


Yes, I have done this and it works successfully for my business application. I modified the Model.tt file to have virtual ObservableCollection<T> instead of ICollection<T> and replaced the HashSet<T> with the same.

I also implemented INotifyPropertyChanged on the entities with the following implementation:

public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
    var handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

I needed to include three extra using statements:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.ObjectModel;

This is the function that I changed in the CodeStringGenerator to implement my getters and setters: (Sorry, I still haven't come around to making this more readable)

public string Property(EdmProperty edmProperty)
{
    var fourSpaces = "    ";
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1} _{2};{3}{4}{0} {1} {2}{3}{4}{{{3}{4}{4}{5}get {{ return _{2}; }} {3}{4}{4}{6}set{3}{4}{4}{{{3}{4}{4}{4}if (value == _{2}) return;{3}{4}{4}{4}_{2} = value;{3}{4}{4}{4}NotifyPropertyChanged();{3}{4}{4}}}{3}{4}}}{3}",
        Accessibility.ForProperty(edmProperty),
        _typeMapper.GetTypeName(edmProperty.TypeUsage),
        _code.Escape(edmProperty),
        Environment.NewLine,
        fourSpaces,
        _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
        _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

And this is a sample full generated entity file for reference:

namespace Eagl.Eagle.Data
{
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;

    public partial class Game : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public Game()
        {
            this.Playtests = new ObservableCollection<Playtest>();
        }

        public int _Id;
        public int Id
        {
            get { return _Id; } 
            set
            {
                if (value == _Id) return;
                _Id = value;
                NotifyPropertyChanged();
            }
        }

        public string _Name;
        public string Name
        {
            get { return _Name; } 
            set
            {
                if (value == _Name) return;
                _Name = value;
                NotifyPropertyChanged();
            }
        }


        public virtual ObservableCollection<Playtest> Playtests { get; set; }
    }
}