Search code examples
c#entity-frameworksavechanges

Intercept and Redirect Entity save from within SaveChanges


I currently have a table in the database that is structured like the following:

Orders
    OrderId int , --Primary Key
    CustomerId int,
    PartId int,
    OrderDate Date,
    Quantity int

Unfortunately because OrderId was set as the primary key instead of having a composite key of CustomerId, PartId, and OrderDate we have many instances of the following in the database:

OrderId | CustomerId | PartId | OrderDate  | Quantity
1       | 23         | 45     | 2016-11-16 | 1
2       | 23         | 45     | 2016-11-16 | 1
3       | 23         | 45     | 2016-11-16 | 1
4       | 26         | 30     | 2016-10-25 | 1
5       | 26         | 30     | 2016-10-25 | 1

Instead of:

OrderId | CustomerId | PartId | OrderDate  | Quantity
1       | 23         | 45     | 2016-11-16 | 3
4       | 26         | 30     | 2016-10-25 | 2

I'm not authorized to update the database due to regulatory/logging issues and an update would break legacy code. I however have convinced my boss to allow me to update Entity to enforce these constraints on any data moving forward.

My idea is to override SaveChanges in DbContext so that if the legacy code tries to add another row, where the CustomerId, PartId, and OrderDate already exists, that save is cancelled and the Quantity column of the existing row is increased. So far I have the following but am unsure how to cancel the save, update the existing row and force that row to save from within SaveChanges.

public override int SaveChanges()
{

    var PartAssemblyList = ChangeTracker.Entries()
                            .Where( x => x.Entity is Order &&
                                         x.State == EntityState.Added);

    foreach (var entity in PartAssemblyList)
    {
        if (/*RowExists*/)
        {
            //Cancel Save (entity.State = EntityState.Unchanged)?
            //Update Quantity of Existing Row
            //Set existing row to save
        }
    }

    return base.SaveChanges();
}

Solution

  • I think that overriding EF is a bad idea because you could use it with other entities other than Orders. However why don't you wrap the SaveChanges() call and make the add/update job yourself ?

    public void AddUpdateOrder(Order o)
    {
        using(var ctx = new YourDataModelContext())
        {
            if(ctx.Orders.Any(x => x.OrderId == o.OrderId && x.CustomerId == o.CustomerId && x.PartId == o.PartId && x.OrderDate == o.OrderDate))
            {
                var e = ctx.Orders.Where(x => x.OrderId == o.OrderId && x.CustomerId == o.CustomerId && x.PartId == o.PartId && x.OrderDate == o.OrderDate).FirstOrDefault();
                e.Quantity += 1;
                ctx.Entry(e).State = Modified; 
            }
            else
            {
                Order e = new Order() { OrderId = o.OrderId, CustomerId = o.CustomerId, PartId = o.PartId, OrderDate == o.OrderDate, Quantity = 1};
                ctx.Orders.Add(e);
            }
            ctx.SaveChanges();
        }
    }