Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working with an ObservableCollection based on an Entity Framework one to many property

Working with WPF in MVVM. I have a ViewModel with a CurrentItem property. This is an Item object which is pulled directly from Entity Framework. Item has a collection of Property objects.

public virtual ICollection<Property> Properties { get; set; }

In the View, I need users to be able to add and remove objects from this collection. To do that, I need to create an ObservableCollection<Property>, which we'll call ItemProperties.

There are various ways to do this. The most obvious is to add a ObservableCollection<Property> property on the ViewModel. Then populate this in the constructor, like so:

ItemProperties = new ObservableCollection<Property>(CurrentItem.Properties);

It's also possible to create an ObservableCollection wrapper that sits over the top of the real collection:

public ObservableCollection<Property> ItemProperties
{
    get
    {
        return new ObservableCollection<Property>(CurrentItem.Properties);
    }
    set
    {
        CurrentItem.Properties = value.ToList();
        OnPropertyChanged("ItemProperties");
    }
}

Which has its own problems. You can't just Add() to this collection, since it'll get first, meaning the collection remains unchanged. So you'd either have to spin up a new collection, add to that, and then assign its value to the property or raise the OnPropertyChanged event outside the property. Either of which also sounds like a maintenance issue.

Is there a more effective way of doing this, which allows you to access the EF property list directly?

like image 474
Bob Tway Avatar asked Oct 12 '15 09:10

Bob Tway


2 Answers

On this you have advantage of decoupling between data layer and Presentation , No need to spin up the collection.

Try a LoadedEvent to load data from the server. Sample event is below

 private ObservableCollection<Property> _itemProperties;

    public ObservableCollection<Property> ItemProperties
    {
        get { return _itemProperties; }
        set
        {
            _itemProperties= value;
           RaisePropertyChanged(() => ItemProperties);
        }
    }

The loaded event

var result= await Task.Run(() => MyBusiness.GetMyData());
//Map to the viewModel if necessary

ItemProperties = result;

Add to the collection

var isSuccess = await Task.Run(()=>MyBusiness.Insert(x));
if(isSuccess)
{
   ItemProperties.Add(x);
}
like image 56
Eldho Avatar answered Sep 29 '22 04:09

Eldho


If you have access to your DbContext in your ViewModel class, you can use DbSet<TEntity>.Local property which it will give you an ObservableCollection<TEntity> that contains all Unchanged, Modified and Added objects that are currently tracked by the DbContext for the given DbSet, but first you need to filter to load into memory only the PropertyItems that belong to your CurrentItem.

public class YourViewModel
{
    private context=new YourContext();

    public YourViewModel()  
    {
      context.ItemProperties.Where(ip=>ip.ItemId==CurrentItem.Id).Load();
      ItemProperties=context.ItemProperties.Local;
    }

    private ObservableCollection<Property> _itemProperties;

    public ObservableCollection<Property> ItemProperties
    {
        get { return _itemProperties; }
        set
        {
           _itemProperties= value;
           OnPropertyChanged("ItemProperties");
        }
    }

    public void SaveItemProperties()
    {
      context.SaveChanges();
    }
}

To save the changes the only you need to do is create, for example, a command that calls the SaveItemProperties method. Also, it could be a good idea disable lazy loading to not load twice the ItemProperties related to your CurrentItem.

If you need to understand more about how this works you can read this article.

like image 39
octavioccl Avatar answered Sep 29 '22 04:09

octavioccl