What is the correct way to insert/update a row using Entity Framework?
I know this 3 methods:
1 - using Attach
var newSale = _context.ST_Sales.FirstOrDefault(x => x.SaleId == saleId);
newSale.Hours = 2;
_context.ST_Sales.Attach(newSale);
_context.ObjectStateManager.ChangeObjectState(newSale, EntityState.Modified);
_context.SaveChanges();
2 - using ApplyCurrentValues
var newSale = _context.ST_Sales.FirstOrDefault(x => x.SaleId == saleId);
newSale.Hours = 2;
_context.ST_Sales.ApplyCurrentValues(newSale);
_context.SaveChanges();
3 - using EF logic
var newSale = _context.ST_Sales.FirstOrDefault(x => x.SaleId == saleId);
newSale.Hours = 2;
_context.SaveChanges(); // EF tracks the newSale obj and knows that was changed
And to insert there are one more method witch uses the AddObject
var newSale = new ST_Sales();
newSale.Hours = 2;
_context.ST_Sales.AddObject(newSale);
_context.SaveChanges();
The problem I'm facing is an error on the EF saying that the modified row is still in memory.
My scenario:
- 20 users can add sales to their own company.
I have a simple table relation:
tbl_Companies { CompanyId, Name }
tbl_Sales { CompanyId, Hours, Amount }
my Edit View
simple get's a sales collection
of the current week as
var model = _context.ST_Sales.Where(x => x.CompanyId == 2);
return View(model);
and on the HttpPost
Action I simply loop through each one and save the db changes like
[HttpPost]
public ViewResult Index(List<ST_Sales> model)
and
bool newSale = false;
foreach( var s in model ) { // let's loop through all
newSale = false;
var sale = _context.ST_Sales.FirstOrDefault(x => x.SaleId == s.SaleId);
if( sale == null) {
// sale was not found, let's add it (this happens for todays date
// as there is no info yet in the db, but we need to add today's info)
sale = new ST_Sale();
sale.Company.Id = currentLoggedInUser.companyId;
newSale = true; // let's say it's a new sale so we can add the new object to the changes to commit
}
sale.Hours = s.Hours;
sale.Amount = s.Amount;
if( newSale ) {
// it's a new sale, let's add the obj to our Entity
_context.ST_Sales.AddObject(sale);
}
else {
// no need to do anything, EF is tracking the changes of existing objects
}
}
_context.SaveChanges();
This works fine for one user... but if I logout and login with a different user, and I can see in the breakpoint that the companyId
value is different, soon I commit the changes to be saved I get the weird error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
What am I doing wrong?
I'm creating a new instance in the Controller level like:
public class SalesController : Controller
{
private SalesTrackerRepository db = SalesTrackerRepository.Instance;
...
}
and using the source from Sams Code
Also, I used Rick Strahl code and ending up using:
public class SalesController : Controller
{
private SalesTrackerRepository db =
SalesTrackerRepositoryFactory.GetWebRequestScopedDataContext<SalesTrackerRepository>();
....
}
And I keep getting the same error :(
My tests are based in the same machine using Chrome and Opera with both different credentials.
How can I get this error on such test?