Sanjeewa
Sanjeewa

Reputation: 555

Entity Framework not saving records on Attach - saves duplicate navigation properties on Add

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;
    }
}

Upvotes: 1

Views: 949

Answers (1)

Reza Aghaei
Reza Aghaei

Reputation: 125197

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:

Upvotes: 3

Related Questions