1

I am trying to do this for my project Entity Framework Databinding with WinForms but instead of using single form, I'm using 2 forms.

CategoryDataGridView is displayed on the Form1, then there is an edit button on Form1, which will load the Form2 which has the ProductsDataGridView (product list of selected category) and a save button to save the changes

I have the following code on my Form2

ProductContext _context = new ProductContext();
public int SeletedCategID { get; set; }

private void Form2_Load(object sender, EventArgs e)
{
    _context.Products.Where(c => c.CategoryId ==     SeletedCategID).ToList();
    productsBindingSource.DataSource =     (_context.Products.Local).ToList();
}

private void SaveBtn_Click(object sender, EventArgs e)
{
    this.Validate();

    foreach (var product in _context.Products.Local.ToList())
    {
        if (product.Category == null)
        {
            _context.Products.Remove(product);
        }
    }
    this._context.SaveChanges();

    this.productsDataGridView.Refresh();

    Form1 frm1 = (Form1)Application.OpenForms["Form1"];
    frm1.Activate();
    frm1.Refresh();
    this.Dispose();
}

my problem now is only edits on the products are saved to DB. Add & Delete are not saved to database.

Henry
  • 195
  • 1
  • 16
  • Are you sure you will see changes at all in `Form1`? I assume it's got its own context with its own cached entities. – Gert Arnold May 31 '16 at 21:03

2 Answers2

2

You should set context.Products.Local as data source of the binding source, while you set context.Products.Local.ToList().

context.Products.Local gets an ObservableCollection<Product> which will stay in sync with context and so all entities changes (add/remove/edit) will tracked by context.
When you use context.Products.Local.ToList() then you are using a List<T> which will not stay in sync with context and Add/Remove will not be tracked and when calling SaveChanges your Adds and Removes will not be saved, while Edits will be saved.

DbSet<TEntity>.Local 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.

Code:

You can load data this way:

context.Products.Where(x => x.CategoryId == SelectedCategoryId).ToList();
this.productBindingSource.DataSource = context.Products.Local;

And you can save data this way:

this.Validate();
this.productBindingSource.EndEdit();
context.Products.Local
       .Where(x => x.CategoryId == 0)
       .ToList().ForEach(x =>
       {
           x.CategoryId = SelectedCategoryId;
       });
context.SaveChanges();
this.DialogResult = DialogResult.OK;

You also don't need that part of code which removes products having null category, because you are not using master-detail. It's an editable list.

As another side-note don't forget to reload data in first form, after you closed second form. Since you are using different contexts in your forms, they are not aware of changes in other context.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Hi Reza, thanks for the detailed answer.. however upon trying your solution to set the `productBindingSource.DataSource` to `Products.Local` the edits and deletes are saved to DB but the Add didn't saved to DB and is also displayed a `DataGridView Default Error Dialog` error. below are the message: The following exception occurred in the DataGridView: System.IndexOutOfRangeException: Index 3 does not have a value. at System.Windows.Forms.CurrencyManager.get_Item(Int32 index) at System.Windows.Forms.DataGridView.DataGridViewDataConnection.GetError(Int32 rowIndex) and is displayed 7 times. – Henry Jun 02 '16 at 13:09
  • 1
    Probably it's because you didn't set a valid `CategoryId` for new records. Don't show `CategoryId` column in the grid, instead when saving changes, set `CategoryId` of all products to `SelectedCategoryId`. – Reza Aghaei Jun 02 '16 at 13:41
  • The solution is what I said. It's tested and works properly :) – Reza Aghaei Jun 02 '16 at 13:49
  • tried your suggestion to remove the categoryID column, also removed the CategoryName column, but how do i set the Category of all products to SelectedCategoryID upon saving bro? – Henry Jun 02 '16 at 13:52
  • Yes please.. its been a week since I encountered my problem (although posted it late), I was trying to do this `_context.Products.CategoryID = SelectedCategID` however `CategoryID` is not included on the `_context.Products` suggestion in intellisense. – Henry Jun 02 '16 at 14:03
  • 1
    If you didn't include foreign keys in model, remove models from edmx and update your models again and include foreign keys. – Reza Aghaei Jun 02 '16 at 14:04
  • Thats it @RezaAghaei you a legend bro.. your updated answer resolves the problem.. i will now move this to my project.. thank you very much bro.. – Henry Jun 02 '16 at 14:16
0

This can not be work, you must delete the product and not the local!

        foreach (var product in _context.Products.ToList())
        {
            if (product.Category == null)
            {
                _context.Products.Remove(product);
            }
        }
        this._context.SaveChanges();

if you want to delete only the local then:

_context.Locals.Remove(local);

I do not know what you are doing with the load object list but if you are trying do change or delete a product or a local entity which is not found in the DbContext or it is a copy of an entity then this will not work. You need the orginal retrieved entity.

Bassam Alugili
  • 16,345
  • 7
  • 52
  • 70