1

I'm developing a Windows Form app with Entity Framework. This is a layered application which has a common layer for the db handling. I have written a SaveSOHeader method to save Sales Order Headers which has an issue:

It doesn't save SalesOrderHeader records to the database table or makes duplicate records in foreign key tables.

I populate all the properties values in the Form code just before saving. This is my model classes:

public class SalesOrderHeader
{
    public int Id { get; set; }
    public System.DateTime OrderDate { get; set; }
    public virtual Customer Customer { get; set; }
    public virtual SalesPerson SalesPerson { get; set; }
    // Other properties stripped to keep it simple
}
public class Customer
{
    public int Id { get; set; }
    public string CustName { get; set; }
    public virtual ICollection<SalesOrderHeader> SalesOrderHeaders { get; set; }
    // Other properties stripped to keep it simple
}
public class SalesPerson
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<SalesOrderHeader> SalesOrderHeaders { get; set; }
    // Other properties stripped to keep it simple
}

To populate the properties only for sales order header I used this code:

private SalesOrderHeader GetUIData()
{
    SalesOrderHeader _soheader = new SalesOrderHeader();
    try
    {
        _soheader.OrderDate = dtOrderDate.Value;
        _soheader.Customer = new Customer() { CustName = cmbCustomerName.Text};
        _soheader.SalesPerson = _SalesPersonMgr.LoadByName(cmbSalesPerson.Text);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message.ToString());
    }
    return _soheader;
}

The question is this:

The following code does not save SalesOrderHeader record when I use Attach method, and when I changed it to db.SalesOrderHeaders.Add(SO);, records save with duplicates to customer, sales person tables etc, so it looks like db.SalesOrderHeaders.Add is an invalid solution.

I Need some expert advice to resolve this issue.

public void SaveSOHeader(SalesOrderHeader SO)
{
    try
    {
        using (SODBContext db = new SODBContext())
        {
            db.SalesOrderHeaders.Attach(SO); // → Doesn't save 
            //db.SalesOrderHeaders.Add(SO);  // → Saves but duplicates foreign key entities 
            db.SaveChanges();
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Sanjeewa
  • 555
  • 1
  • 9
  • 22
  • Since the new instance of the context is not tracking changes, you need to let the change tracker know about the state of entities, for example `db.Entry(entity).State = EntityState.Modified`. – Reza Aghaei Dec 31 '19 at 05:41
  • @RezaAghaei when adding the code you mentioned error comes as 'object' does not contain a definition for 'State' and no accessible extension method 'State' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) – Sanjeewa Dec 31 '19 at 05:48
  • @Sanjeewa Since you are newing up an object you can't use attach. `Attach results in an Unchanged EntityState` To know the difference between attach and add, checkout this answer... https://stackoverflow.com/a/3920217/4381062 – ash Dec 31 '19 at 06:15
  • Stripped unnecessary code from models to make it simpler. You can find the whole code in the [reviews](https://stackoverflow.com/posts/59540010/revisions). – Reza Aghaei Jan 01 '20 at 14:28

1 Answers1

3

You are using different context for loading and saving data, which means you are working in disconnected mode.

In disconnected mode, the entities, have not been tracked by context and when you try to save them you need to tell the context about their tracking state by setting the state yourself using Entry method and State property or correctly using a combination of Add for new entities and Attach for existing entities.

Here is the problem in your case:

  • In case of using Attach(model) you are saying the model is not modified so it's expected to not save it.

  • In case of using Add(model), since the model and all its related models are considered as new, context will save them all into database which results in similar records (with different id) in database for foreign keys.

Prevent inserting duplicate related entities

For foreign keys, usually you don't need to set the whole navigation property and it's enough to set the foreign key property:

//using System.Data.Entity;
var product = new Product();
product.CategoryId = 1;
product.Name = "Product 1000";
product.Price = 1000;
product.Description = "Product 1000 Description";
using (var db = new TestDBEntities())
{
    db.Entry(product).State = EntityState.Added;
    db.SaveChanges();
}

But if for any reason you want to use the navigation property, you need to set it as unmodified:

//using System.Data.Entity;
var product = new Product();
product.Category = new Category() { Id = 1, Name = "Category 1" };
product.Name = "Product 1000";
product.Price = 1000;
product.Description = "Product 1000 Description";
using (var db = new TestDBEntities())
{
    db.Entry(product.Category).State = EntityState.Unchanged;
    db.Entry(product).State = EntityState.Added;
    db.SaveChanges();
}

Further reading

To read more about the issue, you can take a look at the following articles:

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • still having the problem of 'object' does not contain a definition for 'State' and no accessible extension method 'State' accepting a first argument of type 'object' when adding ```db.Entry(product).State = EntityState.Added;``` – Sanjeewa Dec 31 '19 at 08:03
  • 1
    The code has been tested using EF 6. I'm not sure what the mistake you have in the code. Just make sure you have added `using System.Data.Entity` and make sure you haven't mistakenly created any `Entry` extension method or something. What I have in the answer, has copied from a working code. I expect it should be a simple mistake. – Reza Aghaei Dec 31 '19 at 08:05
  • Accidentally I have added ```internal object Entry(object salesOrderHeaders) { throw new NotImplementedException(); } ``` without knowing , that was the reason – Sanjeewa Dec 31 '19 at 08:19
  • 1
    Added some links for further reading about disconnected mode and state management. – Reza Aghaei Jan 01 '20 at 15:29