1

I inherited a project where the data model is created based on the database using EF 6. I'm creating a viewmodel as follows, where Data is the type that is autogenerated for my database context (i.e. it contains the fields corresponding to the tables in the DB).

public class ViewModel : INotifyPropertyChanged
{
  private Data _data = new Data();

  private ObservableCollection<Order> _orders;
  public ObservableCollection<Order> Orders
  {
    get { return _orders; }
    set
    {
      _orders = value;
      OnPropertyChanged("Orders");
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged(String propertyName = null)
  {
    var handler = PropertyChanged;
    if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  }
}

Binding the grid in the XAML code shouldn't be hard and only requires me to point to the property Orders. However, I don't see exactly how the property will be populated from the DB (I haven't seen any Load method) nor how the DB will be updated (if the grid value is changed and the binding set to TwoWay).

I've googled it but didn't find anything spot-on. Suggestions?

Data is autogenerated in the following class.

public partial class Data : DbContext
{
  public Data() : base("name=Data") { }

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    throw new UnintentionalCodeFirstException();
  }

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

The problem is that when I create an instance of Data, the property Orders seem to be null and I'm not sure how to populate it. There's no methods for loading, enumerating, selecting, where'ing etc... It's of type IObservable, not IEumerable and I can't see any ToList or ToEnumerable methods neither...

Edit

As I try to load in different tables into the set, I notice that one of them is null for no apparent reason.

enter image description here

Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438
  • From your `Data` class, do you have access to the `DbSet` you have declared in your context? – ocuenca Aug 05 '15 at 16:53
  • @octavioccl If I understand you correctly, then yes. The *Data* class **is** my database context and the field *Orders* is a database set. Please see the edition. – Konrad Viltersten Aug 05 '15 at 16:57
  • Why are you using `IObservable` type instead `DbSet` in the declaration of your `Order` property. You should use `DbSet`. – ocuenca Aug 05 '15 at 17:20
  • @octavioccl It's automatically generated for me by the TT script that way. I haven't question it, because of that. – Konrad Viltersten Aug 05 '15 at 21:49
  • Well, you could change that type in your T4 script or you could use the first solution that I propose using the `Set` method. – ocuenca Aug 05 '15 at 21:52

2 Answers2

2

Using the Data class, you could create a default constructor to set the Orders property in your ViewModel as I show as follow:

public class ViewModel : INotifyPropertyChanged
{
  private Data _data = new Data();

  private ObservableCollection<Order> _orders;
  public ObservableCollection<Order> Orders
  {
    get { return _orders; }
    set
    {
      _orders = value;
      OnPropertyChanged("Orders");
    }
  }

  //define a constructor
  public ViewModel()
  {
    _data.Set<Order>().Load();
    this.Orders=_data.Set<Order>().Local;
  }
 //...
}

Local is a property of the DbSet<T> class and It gets an ObservableCollection<T> that represents a local view of all Added, Unchanged, and Modified entities in this set. This local view will stay in sync as entities are added or removed from the context. Likewise, entities added to or removed from the local view will automatically be added to or removed from the context.

Now, due to DbSet<TEntity>.Local gives you objects that are currently tracked by the DbContext, you need to load first-into memory- the entities you need to bound. That's way you need to call the Load method first, to materialize your query.

Finally, WPF natively supports binding to an ObservableCollection so there is no additional code required to have two way data binding with full support for WPF sorting, filtering etc.

Update 1

If you change the type of your Orders property to DbSet<Order> in the Data class, then you can set the Orders property of your ViewModel this way:

    _data.Orders.Load();
    this.Orders=_data.Orders.Local;
ocuenca
  • 38,548
  • 11
  • 89
  • 102
  • This was actually a very interesting reply. I tested to load order before but it didn't work (got null there). Now that you pointed it out, I tried again and took a look at the different tables. It seems that *Orders* is faulty somehow (see the second edit in my question), while the others load in as supposed to. What can be the cause of it? (+1 for clear/extensive reply and a possible bounty if we get it through all the way) – Konrad Viltersten Aug 05 '15 at 22:08
-1
  1. Lets clarify that you are using DataGrid (not just Grid). If you add your XAML code it will be useful

  2. I don't know any method on DataGrid and usually you can populate collection in constructor or inside property

    public ObservableCollection Orders
    {
    get {
    if(_orders == null) { // populate orders here}
    return _orders; }
    set { _orders = value;
    OnPropertyChanged("Orders"); }
    }

  3. To save changes you can take a look on this post

Community
  • 1
  • 1
AntonS
  • 341
  • 1
  • 8