Search code examples
entity-frameworkasp.net-mvc-5dbcontextef-database-first

EF 6, adding entity to DbContext creates duplicate entity


I have a web application (MVC 5, EntityFramework 6). It's connected to an SQL database via a DbContext. I'm having an issue where adding a new entity object creates a duplicate entry in the entity set (but not the DB) and I'm not sure how to stop this from happening.

Controller, whose method is called via an ajax request:

public class CustomerController : Controller
{
    MyDBEntities db = new MyDBEntities();  //DbContext

    public ActionResult SaveStuff(string customerId, string stuff)
    {
        Customer customer = db.Single(c => c.ID.Equals(customerId));
        Stuff stuff = new Stuff(stuff, customer);
        db.Stuffs.Add(stuff);
        db.SaveChanges();

        return PartialView("MyControl", customer);
    }
}

There is a 1-to-many association between Customer and Stuff, and there is a "Stuffs" navigation property in Customer.

Stuff includes fields that are int, string, and DateTime.

The controller method returns a PartialView which is used by JavaScript to refresh the contents of a control.

The "MyControl" control does this:

var stuffs = Model.Stuffs.OrderByDescending(...);

When the control is rendered in this situation, Model.Stuffs contains a duplicate entry. There's an entry with a name of Stuff (probably the new object created in the control method) as well as well as an entry with a name of System.Data.Entity.DynamicProxies.Stuff_<uuid> which is the same exact data (I imagine read from the DB).

This is only a problem when I'm writing into and then reading from an entity set within the same web request. Other/future web requests that cause a read are fine. How can I make this work correctly?


Solution

  • This is happening because the DateTime object is losing precision when it is written into the SQL database (see: SQL Server DateTime vs .NET DateTime). When read back from the DB, it has a different value and therefore does not overwrite the existing "stuff" object that still exists locally in db.Stuffs.

    A simple solution is to change the DateTime's setter for Stuff to private and add your own pseudo-setter function that has the rounding built into it:

    public void SetTimestamp(DateTime timestamp)
    {
        //Precision in SQL is lower than in .NET, so just round to tenth seconds
        this.Updated = timestamp.AddTicks(- (timestamp.Ticks % (TimeSpan.TicksPerSecond / 10)));
    }
    

    Using DateTime2 in the SQL database (Server 2008+) is also an option should you need to maintain that level of precision.