all, I'm new here. My problem is how to invoke savechanges two times in one transaction.
Below is my code:
var order = new Order();
// modify order's properties
db.orders.add(order);
db.SaveChanges();
db.Entry(order).reload();
// do some other work about the order entity
db.Entry(order).state = EntityState.Modified;
db.SaveChanges();
But the second SaveChanges failed. The error message is "affects the unexpected number of rows (0).The entity may be modified or deleted after loading.Refresh ObjectStateManager".
My question is how to make the second SaveChanges() works. Because the property code in order entity is an autoincrease custom string field base on a custom function in database.
Thanks
Your call to .Reload() is not needed, and as #maxlego mentioned setting the .State to modified should not be needed if you haven't changed AutoDetectChangesEnabled from its default of true.
You should be able to follow this pattern and get the desired result. You can do the following with or without the TransactionScope. The benefit of using TransactionScope is that if your second call to SaveChanges() fails with an exception then the changes made by the first call will be rolled back. On the other hand, if you want the first call of SaveChanges() to succeed even when the second call fails you should remove the TransactionScope code.
using (var db = new MyContext())
using (var scope = new TransactionScope())
{
var order = new Order();
// modify order's properties
db.orders.add(order);
db.SaveChanges();
// make additional changes...
// E.g. assuming order.ID is an auto-incrementing primary key
// that is determined by the database I can now use the
// value the database set to log this change...
// save changes
db.SaveChanges();
// commit the transaction
scope.Complete();
}
Related
I am looping through the rows in a database table and updating the account balance of each row if it meets the required conditions.
Instead of the row that meets the condition to be updated alone, it gets updated and a new row with the updated values is also created at the same time with the same query.
I use this same code in the same app to change account balances of single rows and it works perfectly. But when I make calls to it from a foreach loop, the above stated problem occurs:
public bool CreditCustomerAccount(CustomerAccount account, decimal amount)
{
try
{
if (account.Type == AccountType.Current || account.Type == AccountType.Savings)
{
account.AccountBalance += amount;
}
else
{
account.AccountBalance -= amount;
}
_context.Entry(account).State = EntityState.Modified;
_context.SaveChanges();
return true;
}
catch (Exception)
{
return false;
}
}
It updates the row in question but also creates a new database table entry with the updated details
When you post an entity directly back, EF context knows nothing about it. Check out the Attach method on a DBSet. From MS DOCS:
Attaches the given entity to the context underlying the set. That is,
the entity is placed into the context in the Unchanged state, just as
if it had been read from the database... Attach is used to repopulate
a context with an entity that is known to already exist in the
database.
SaveChanges will therefore not attempt to insert an attached entity
into the database because it is assumed to already be there.
So using Attach before modifying any properties will allow the context to "know" about the object, and any changes would then indicate the object is modified.
I am trying to use TryUpdateModel to restrict the properties updated by the edit method.
I want users to see all the fields in the Edit form but don't want the Title property of the Album entity to be updated. So I'm doing this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit()
{
var album = new Album();
TryUpdateModel(album, new[] { "Price","Date","Genre"});
if (ModelState.IsValid)
{
db.Entry(album).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}.....
When executing this code, I am having an error on db.SaveChages(), which says:
Store update, insert, or delete statement affected an unexpected
number of rows (0). Entities may have been modified or deleted since
entities were loaded. See
http://go.microsoft.com/fwlink/?LinkId=472540 for information on
understanding and handling optimistic concurrency exceptions.
What am I doing wrong here?
Basically, it's saying that 0 rows were modified by your update. So, your primary key (whatever makes a row unique) points to an ID that does not exist in the database. You need to figure out why this is happening and instead use a key that exists in the database.
I'm debugging this method for two cases: one where there is a parent, the other where there is no parent.
If there is no parent, the new Person has an id of 0 but never actually gets saved to the db.
If there is a parent, the new Person has an id of 0 in this method, but a new record is inserted into the db with the correct value (one more than the highest in the table).
What is going on here? I know I'm doing something wrong, I'm just not sure what.
I'm using EF Codefirst.
The code for the controller method:
[HttpPost]
public ActionResult Create(CreatePersonViewModel viewModel)
{
if (ModelState.IsValid)
{
var parent = _db.Persons.FirstOrDefault(s => s.PersonId == viewModel.ParentId);
var person = new Person() { Name = viewModel.Name };
// if it has a parent, build new relationship
if (parent != null)
{
person.Parent = parent;
parent.Children.Add(person);
};
_db.Save();
return RedirectToAction("detail", "person", new { personId = person.PersonId });
}
return View(viewModel);
}
If there is no parent, the new Person has an id of 0 but never actually gets saved to the db.
That's because you never tell EF that it should persist the entity. You only create a new Person() and that's it.
You should do:
dbContext.AddToPersons(person);
before calling dbContext.SaveChanges().
In the case when there is a parent, person is saved because of its relationship with parent.
Update
Just occurred to me: If you're doing code first you might not have the AddToPersons(...) method available on the data context. If this is so, you can use dbContext.Persons.AddObject(person) instead.
The fact with you are referring to is auto increment ID for you object. It is controlled by your ORM. You may want to check this question
You may want to check this link from msdn
Remarks Refresh has the dual purpose of allowing an object to be
refreshed with data from the data source and being the mechanism by
which conflicts can be resolved. For more information, see Saving
Changes and Managing Concurrency (Entity Framework). The order in
which objects are refreshed is nondeterministic. After the Refresh
method is called, the object’s original values will always be updated
with the data source value, but the current values might or might not
be updated with the data source value. This depends on the
RefreshMode. The StoreWins mode means that the object should be
updated to match the data source values. The ClientWins value means
that only the changes in the object context will be persisted, even if
there have been other changes in the data source. To ensure that an
object has been updated by data source-side logic, you can call the
Refresh method with the StoreWins value after you call the SaveChanges
method.
I have this code in a Windows Service targeted to .Net 4.5 that uses a database-first Entity Framework layer:
var existingState = DataProcessor.GetProcessState(workerId);
existingState.ProcessStatusTypeId = (int)status;
existingState.PercentProgress = percentProgress;
existingState.ProgressLog = log;
DataProcessor.UpdateProcessState(existingState);
And this code in a data processing class in the same solution:
public ProcessState GetProcessState(int id)
{
using (var context = new TaskManagerEntities())
{
var processes = (from p in context.ProcessStates.Include("ProcessType").Include("ProcessStatusType")
where p.IsActive && p.ProcessStateId == id
select p);
return processes.FirstOrDefault();
}
}
public ProcessState UpdateProcessState(ProcessState processState)
{
using (var context = new TaskManagerEntities())
{
context.ProcessStates.Add(processState);
context.Entry(processState).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
return processState;
}
ProcessState is a parent to two other classes, ProcessStatusType and ProcessType. When I run that code in the windows service, it retrieves a record, updates the entity and saves it. Despite the fact that the ProcessType child is never used in the above code, when the save on the ProcessState entity is performed, EF does an insert on the ProcessType table and creates a new record in it. It then changes the FK in the ProcessStatus entity to point it at the new child and saves it to the database.
It does not do this in the ProcessStatusType table, which is set up with an essentially identical FK parent-child relationship.
I now have a database full of identical ProcessType entries that I don't need, and I don't know why this is occurring. I feel like I'm making some obvious mistake that I can't see because this is my first EF project. Is the issue that I'm allowing the context to expire in between calls but maintaining the same entity?
Using Add will set the state of all elements to Added, which is causing the child elements to be inserted. The parent element is not inserted as you specify EntityState.Modified for this element.
Try using the following in the UpdateProcessState rather than using Add.
context.ProcessStates.Attach(processState);
context.Entry(processState).State = EntityState.Modified;
context.SaveChanges();
Attach will set the state of all elements to Unchanged and by specifying Modified for the parent element you are indicating that only this element should be updated.
On another note. You should use the strongly-typed Include(x => x.ProcessType) rather than Include("ProcessType").
i'm trying to implement a Repository and UnitOfWork patterns using Entity Framework.
This is the scenario:
An user can add or modify detail records in a master-detail window and when hit "save" the added/modified records are sent to the server.
Then i perform a CreateOrUpdateMultiple().
CreateMultiple add the new records to the repository.
UpdateMultiple retrieve records that going to be updated.
After the two operations completes i need to update the master record with a sum(field) with all detail records. (With all i mean the existing ones that were not modified and the ones in memory)
This is what i have thought so far...
Being purist with repository pattern i should retrieve all detail records and then should mix in one list the existing records (modified or not) and the addded ones, and then do the sum operation, but what if the amount field of detail records is a database calculated field?
Read from database only records to be updated (thinking this will be faster because if i have 40 records and just 3 are modified and 2 added i will not read the entire set) and then somehow perform the update to the master record, but the problem is those records aren't yet in the database.
I have just one ObjectContext instance for all operations and i call SaveChanges() in my service to commit all in just one transaction.
What do you advice me to do? Or how do you archieve this kind of situation?
Thanks in advance
//Update
Here more technically described
The is what i have right now using transactionScope... and this is what i'm trying to avoid because of all the calls to database
//Service Layer
Method()
{
Method1.Invoke(masterRecordId, detaildRecords); //
}
//Business Layer
Method1(masterRecordId, detailRecords)
{
using(TransactionScope ts = new TransactionScope())
{
var recordsToUpdate = dal.RetrieveOnlyRecordsToUpdate();
//Update retrieved records with the values of recods comming from the client
dal.Update(recordsToUpdate); //ctx.ApplyChanges(); and ctx.SaveChanges();
dal.Add(recordsToAdd) //ctx.Add(detail records); and ctx.SaveChanges();
//Update master record TotalSum
dal.UpdateMasterRecord(masterRecordId); //Here is performed ctx.ExecuteStoredCommand("UPDATE MasterTable = SUM() WHERE MasterRecordId = {0}")...
Method2();
ts.Complete();
}
}
Method2(masterRecordId)
{
using(TransactionScope ts = new TransactionScope())
{
MasterRecord m = Retrieve(masteRecordId);
Notification notification = new Notification(){ ...assign properties..., m.TotalSum};
dal.Add(notification); //ctx.Add(notification); and ctx.SaveChanges();
ts.Complete();
}
}
This is what i want to do...
//Service Layer
Method()
{
Method1.Invoke(masterRecordId, detail records);
UnitOfWorkManager.Current.Commit(); //
}
//Business Layer
Metodo1(masterRecordId, detail records)
{
MasterRecord masterRecord = repository.Retrieve(masterRecordId);
var recordsToUpdate = repository.RetrieveOnlyRecordsToUpdate();
//Update retrieved records with the values of recods comming from the client
repository.Modify(recordsToUpdate);
repository.Add(recordsToAdd);
//Here i'm stuck and i'm thinking it should be something like this.
masterRecord.TotalSum = sum(detailRecords in memory + detail records in database); //
repository.Modify(masterRecord); //
or
//Another way somehow...
//Then keep going with the udpated master record
Method2(masterRecord);
}
}
Method2(masterRecord)
{
//Create notification
var notification = new Notification(){ ...properties.., masterRecord.TotalSum};
repository.Add(notification);
}
If you want to do it as transaction and call SaveChanges only once you must do it in your application before SaveChanges is called. So generally you must get sum before you do any change and modify the sum by updated values, inserted values and deleted values. Then you will set the new sum value into the master record. If you do it this way you will have unit of work.
Why other approaches are not so good.
Calculated field in database. This will require manually reloading master entity after the save operation because saving detail cannot reload changes in other entity. Moreover database logic will probably require trigger on detail table to modify sum in master table.
Updating master record after saving changes to details breaks unit of work.