Concurrency exceptions when I tried to modifies two tables in MVC - asp.net-mvc

I am having a concurrency Exception when I tried to update MessageTemplate and TemplateLookup table. MessageTemplate table update fine but the error occur when the code tried to update TemplateLookup table. Any help will be appreciated
Error
"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."
Controller
public async Task<ActionResult> Edit (MessageTemplate messagetemplate,int Id, bool Enable, int id)
{
TemplateLookup template = new TemplateLookup();
var appdata = DateTime.Now;
try
{
if (ModelState.IsValid)
{
var currentUser = await _db.vwUsers.FirstOrDefaultAsync(i => i.DomainUserName == User.Identity.Name);
if (currentUser != null)
messagetemplate.LastUpdatedByUser = currentUser.FullName;
messagetemplate.LastUpdatedByUserID = User.Identity.Name;
messagetemplate.LastUpdatedOn = appdata;
//messagetemplate.Id = Id;
_db.Entry(messagetemplate).State = EntityState.Modified;
//await _db.SaveChangesAsync();
if (Enable == false)
{
var templateidlist = _db.TemplateLookups.Where(v => v.TemplateId == Id).ToList();
messagetemplate.Enable = Enable;
foreach (var templateid in templateidlist)
{
template.TemplateActive = Enable;
template.LastUpdatedOn = appdata;
template.LastUpdatedByUser = currentUser.FullName;
template.TemplateId = Id;
// Error occur here
_db.Entry(template).State = EntityState.Modified;
}
}
_db.Entry(messagetemplate).State = EntityState.Modified;
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(messagetemplate);
}
}

Well as it says - it looks like the row is being modified in between your query being started and actually saved...
I noticed you change the Id:
template.TemplateId = Id;
which you also search on;
var templateidlist = _db.TemplateLookups.Where(v => v.TemplateId == Id).ToList();
So if this action is run twice with the same template Ids it may change them before the second one has a chance to run.
Ususally in this concurrent modification scenario you'd catch the exception and re-display the form with the new values - but it depends on your usecase we can't say without more information about what your application does...
You can read about various options to handle it here: https://learn.microsoft.com/en-us/ef/ef6/saving/concurrency

Related

Handling Next Record nullable reference Controller Code to pass to viewbag

I have researched various nullable reference handling posts, but not finding anything helpful. So what I am doing below to handle this null reference (it's a hack for now to stop the error page from displaying to users) is to essentially return the current id if a next record does not exist in my edit controller.
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var myInClause = new string[] { "a", "c", "k" };
var myQueryResults = await _context.MyClass.FindAsync(id);
int? NextIdObject = (from i in _context.MyClass
where myInClause.Contains(i.RowType) && (i.Id > myclass.Id)
select new { i.Id }).DefaultIfEmpty().First().Id;
if (!NextIdObject.Equals(0))
{
ViewBag.nextID = NextIdObject;
}
else
{
ViewBag.nextID = id;
}
if (myQueryResults == null)
{
return NotFound();
}
return View(myQueryResults);
}
I would prefer to just redirect to the index page (if they hit this error, it means they are done working through a queue anyway, no next record would ever exist here). Or maybe just keep the code as is and display a message on the button to indicate end of list. Any thoughts here. Note, using any +1 increment on the id does not work for me, as I don't need the user to see all id records, just the one's with a/c/k which is why I bring the myInclause variable in. If there is a better way to use the SQL sytanx of "IN" for Linq queries, I am all ears.
I would prefer to just redirect to the index page (if they hit this
error, it means they are done working through a queue anyway, no next
record would ever exist here)
You could use try...catch block simply like
try
{
var myInClause = new string[] { "a", "c", "k" };
var myQueryResults = await _context.MyClass.FindAsync(id);
int NextIdObject = (from i in _context.MyClass
where myInClause.Contains(i.RowType) && (i.Id > myclass.Id)
select new { i.Id }).DefaultIfEmpty().First().Id;
ViewBag.nextID = NextIdObject;
}
catch(Exception ex)
{
return RedirectToAction("Index");
//or return View("Index");
}
return View(myQueryResults);

Best Way to Update only modified fields with Entity Framework

Currently I am doing like this:
For Example:
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
db.Entry(oldobj).CurrentValues.SetValues(model);
}
It works, but for example,
I have 50 columns in my table but I displayed only 25 fields in my form (I need to partially update my table, with remaining 25 column retain same old value)
I know it can be achieve by "mapping columns one by one" or by creating "hidden fields for those remaining 25 columns".
Just wondering is there any elegant way to do this with less effort and optimal performance?
This is a very good question. By default I have found that as long as change tracking is enabled (it is by default unless you turn it off), Entity Framework will do a good job of applying to the database only what you ask it to change.
So if you only change 1 field against the object and then call SaveChanges(), EF will only update that 1 field when you call SaveChanges().
The problem here is that when you map a view model into an entity object, all of the values get overwritten. Here is my way of handling this:
In this example, you have a single entity called Person:
Person
======
Id - int
FirstName - varchar
Surname - varchar
Dob - smalldatetime
Now let's say we want to create a view model which will only update Dob, and leave all other fields exactly how they are, here is how I do that.
First, create a view model:
public class PersonDobVm
{
public int Id { get; set; }
public DateTime Dob { get; set; }
public void MapToModel(Person p)
{
p.Dob = Dob;
}
}
Now write the code roughly as follows (you'll have to alter it to match your context name etc):
DataContext db = new DataContext();
Person p = db.People.FirstOrDefault();
// you would have this posted in, but we are creating it here just for illustration
var vm = new PersonDobVm
{
Id = p.Id, // the Id you want to update
Dob = new DateTime(2015, 1, 1) // the new DOB for that row
};
vm.MapToModel(p);
db.SaveChanges();
The MapToModel method could be even more complicated and do all kinds of additional checks before assigning the view model fields to the entity object.
Anyway, the result when SaveChanges is called is the following SQL:
exec sp_executesql N'UPDATE [dbo].[Person]
SET [Dob] = #0
WHERE ([Id] = #1)
',N'#0 datetime2(7),#1 int',#0='2015-01-01 00:00:00',#1=1
So you can clearly see, Entity Framework has not attempted to update any other fields - just the Dob field.
I know in your example you want to avoid coding each assignment by hand, but I think this is the best way. You tuck it all away in your VM so it does not litter your main code, and this way you can cater for specific needs (i.e. composite types in there, data validation, etc). The other option is to use an AutoMapper, but I do not think they are safe. If you use an AutoMapper and spelt "Dob" as "Doob" in your VM, it would not map "Doob" to "Dob", nor would it tell you about it! It would fail silently, the user would think everything was ok, but the change would not be saved.
Whereas if you spelt "Dob" as "Doob" in your VM, the compiler will alert you that the MapToModel() is referencing "Dob" but you only have a property in your VM called "Doob".
I hope this helps you.
I swear by EntityFramework.Extended. Nuget Link
It lets you write:
db.Person
.Where(x => x.ID == model.ID)
.Update(p => new Person()
{
Name = newName,
EditCount = p.EditCount+1
});
Which is very clearly translated into SQL.
Please try this way
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
// Newly Inserted Code
var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);
db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}
public static object CheckUpdateObject(object originalObj, object updateObj)
{
foreach (var property in updateObj.GetType().GetProperties())
{
if (property.GetValue(updateObj, null) == null)
{
property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
.GetValue(originalObj, null));
}
}
return updateObj;
}
I have solved my Issue by using FormCollection to list out used element in form, and only change those columns in database.
I have provided my code sample below; Great if it can help someone else
// Here
// collection = FormCollection from Post
// model = View Model for Person
var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault();
if (result != null)
{
List<string> formcollist = new List<string>();
foreach (var key in collection.ToArray<string>())
{
// Here apply your filter code to remove system properties if any
formcollist.Add(key);
}
foreach (var prop in result.GetType().GetProperties())
{
if( formcollist.Contains(prop.Name))
{
prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null));
}
}
db.SaveChanges();
}
I still didn't find a nice solution for my problem, so I created a work around. When loading the Entity, I directly make a copy of it and name it entityInit. When saving the Entity, I compare the both to see, what really was changed. All the unchanged Properties, I set to unchanged and fill them with the Database-Values. This was necessary for my Entities without Tracking:
// load entity without tracking
var entityWithoutTracking = Context.Person.AsNoTracking().FirstOrDefault(x => x.ID == _entity.ID);
var entityInit = CopyEntity(entityWithoutTracking);
// do business logic and change entity
entityWithoutTracking.surname = newValue;
// for saving, find entity in context
var entity = Context.Person.FirstOrDefault(x => x.ID == _entity.ID);
var entry = Context.Entry(entity);
entry.CurrentValues.SetValues(entityWithoutTracking);
entry.State = EntityState.Modified;
// get List of all changed properties (in my case these are all existing properties, including those which shouldn't have changed)
var changedPropertiesList = entry.CurrentValues.PropertyNames.Where(x => entry.Property(x).IsModified).ToList();
foreach (var checkProperty in changedPropertiesList)
{
try
{
var p1 = entityWithoutTracking.GetType().GetProperty(checkProperty).GetValue(entityWithoutTracking);
var p2 = entityInit.GetType().GetProperty(checkProperty).GetValue(entityInit);
if ((p1 == null && p2 == null) || p1.Equals(p2))
{
entry.Property(checkProperty).CurrentValue = entry.Property(checkProperty).OriginalValue; // restore DB-Value
entry.Property(checkProperty).IsModified = false; // throws Exception for Primary Keys
}
} catch(Exception) { }
}
Context.SaveChanges(); // only surname will be updated
This is way I did it, assuming the new object has more columns to update that the one we want to keep.
if (theClass.ClassId == 0)
{
theClass.CreatedOn = DateTime.Now;
context.theClasses.Add(theClass);
}
else {
var currentClass = context.theClasses.Where(c => c.ClassId == theClass.ClassId)
.Select(c => new TheClasses {
CreatedOn = c.CreatedOn
// Add here others fields you want to keep as the original record
}).FirstOrDefault();
theClass.CreatedOn = currentClass.CreatedOn;
// The new class will replace the current, all fields
context.theClasses.Add(theClass);
context.Entry(theClass).State = EntityState.Modified;
}
context.SaveChanges();
In EF you can do like this
var result = db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
if(result != null){
result.Name = newName;
result.DOB = newDOB;
db.Person.Update(result);
}
Or you can use
using (var db= new MyDbContext())
{
var result= db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
result.Name= newName;
result.DOB = newDOB;
db.Update(result);
db.SaveChanges();
}
For more detail please EntityFramework Core - Update Only One Field
No Worry guys
Just write raw sql query
db.Database.ExecuteSqlCommand("Update Person set Name='"+_entity.Name+"' where Id = " + _entity.ID + "");

Entity Framework update record not working

I'm new to Entity Framework. I'm trying to update a record and save changes to the database.
public void SaveEdit(Gate gate)
{
try
{
using (dc = new GateEntities())
{
var query = (from tbsite in dc.tblSites
where tbsite.ID == gate.ID
select tbsite).FirstOrDefault();
query.CalledInAt = gate.CalledInAt;
query.CallerRequest = gate.CallerRequest;
query.ContactPersonOnSite = gate.ContactPersonOnSite;
query.Email = gate.Email;
query.EmailSitePerson = gate.EmailSitePerson;
dc.SaveChanges();
}
}
catch (Exception ex)
{
throw ex;
}
}
It gets no exceptions or error messages but it does not save the changes to the database. why it's not updating the record?
After You modify query object You should change it's state to Modified before calling context.SaveChanges(). Your context object should know about the entity that You modify. Assuming dc is Your context object:
query.CalledInAt = gate.CalledInAt;
//change other properties ..
dc.Entry(query).State = EntityState.Modified;
dc.SaveChanges();
That should work for You.
You have to use the entityframework to select your object, with that the result object will be track-able, so try this
using (var dc = new GateEntities())
{
var gate = dc.tblSites.Where(g => g.ID == date.ID).FirstOrDefault();
gate.CalledInAt = gate.CalledInAt;
gate.CallerRequest = gate.CallerRequest;
gate.ContactPersonOnSite = gate.ContactPersonOnSite;
gate.Email = gate.Email;
gate.EmailSitePerson = gate.EmailSitePerson;
dc.SaveChanges();
}
Also noticed this happening when the Entity doesn't have configured a primary Key.
On EF Core check your OnModelCreating and make sure entity.HasKey(e => e.TheKey); is present.
By the documentation you are not required to work with states: https://learn.microsoft.com/en-us/ef/core/get-started/?tabs=netcore-cli
var yourEntity = await context.FirstOrDefaultAsync(e => e.Id = 1);//You can also call FindAsync(id)
yourEntity.Name = "NewName";//This line will let EF Core knows the entity has been modify
await context.SaveChangesAsync();
Although you can check how to work with states so here: https://learn.microsoft.com/en-us/ef/ef6/saving/change-tracking/entity-state
And the code will look like:
var yourEntity = await context.FirstOrDefaultAsync(e => e.Id = 1);
yourEntity.Name = "NewName";
context.Entry(yourEntity).State = EntityState.Modified;
await context.SaveChangesAsync();
They are both the same, I honestly prefer manually setting the state (in big projects) sometimes EF Core loses tracking of some entities (this is a personal experience) so I double check updating the status as Added Or Modified.
Thanks

Adding New Child Object Whilst Modifying Existing Children Entity Framework

I've look at some of the answers to similar questions and they don't really seem to fit mine.
I'm trying to incorporate a pattern from Entity Framework: DbContext(page 90) and it doesn't seem to work. The code that I'm using is given below:
[HttpPost]
public ActionResult Edit(Order order)
{
if (ModelState.IsValid)
{
db.Orders.Add(order);
db.Entry(order).State = EntityState.Modified;
foreach (var orderDetail in order.OrderDetails)
{
if (orderDetail.OrderId == 0)
{
db.Entry(orderDetail).State = EntityState.Added;
}
else
{
db.Entry(orderDetail).State = EntityState.Modified;
}
// The example order that I'm updating has two child entities
// so this orderId will be for the third, added one.
int addedOrderDetailId = order.OrderDetails[2].OrderId;
}
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CustomerId = new SelectList(db.Customers, "CustomerId", "CompanyName", order.CustomerId);
return View(order);
}
I've been running an example where the Order object has two existing OrderDetail objects and I'm attempting to add a third. I included the addedOrderDetailId variable, so that I could add it to the 'Watch' and see when it changed
What I've found is happening is that the OrderId of the added OrderDetail object (which is 0 when the foreach loop is entered) is being updated by entity framework to the OrderId of the Order object. This is happening at after the first iteration through the foreach loop (when the first child entity is having its state changed to modified. This means that all three children are being marked as modified. This is causing SaveChanges() to try to update an entry into the database that doesn't exist.
If anyone else has had this problem, then I would be greatful for any advice as to get around this. I will also have to deal with existing child objects being deleted, but I haven't got around to this yet, so if anyone knows of a pattern for this, that would also be appreciated.
Edit:
After taking Slauma's advice and removing db.Orders.Add(order). I was able to move the call to db.Entry(order).State underneath the foreach loop. This allowed me to loop through the loop and set the state of each OrderDetail object to modified for the existing ones and added for the added one. I then simply had to assign the OrderId of the parent to the OrderId of the child and the update was successful. I've also included the code that I've used to delete child objects during the edit. I'm not sure how efficient this is, but it works. Here is the revised code:
[HttpPost]
public ActionResult Edit(Order order)
{
if (ModelState.IsValid)
{
List<int> previousProductIds = db.OrderDetails
.Where(ep => ep.OrderId == order.OrderId)
.Select(ep => ep.ProductId)
.ToList();
List<int> currentProductIds = order.OrderDetails
.Select(o => o.ProductId)
.ToList();
List<int> deletedProductIds = previousProductIds
.Except(currentProductIds).ToList();
foreach (var deletedProductId in deletedProductIds)
{
OrderDetail deletedOrderDetail = db.OrderDetails
.Where(od => od.OrderId == order.OrderId && od.ProductId == deletedProductId)
.Single();
db.Entry(deletedOrderDetail).State = EntityState.Deleted;
}
foreach (var orderDetail in order.OrderDetails)
{
if (orderDetail.OrderId == 0)
{
db.Entry(orderDetail).State = EntityState.Added;
orderDetail.OrderId = order.OrderId;
}
else
{
db.Entry(orderDetail).State = EntityState.Modified;
}
}
db.Entry(order).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CustomerId = new SelectList(db.Customers, "CustomerId", "CompanyName", order.CustomerId);
return View(order);
}
Remove this line from your code:
db.Orders.Add(order);
This will actually put the order including all orderDetails into Added state. Relationship fixup (which happens automatically in Add) will set the OrderId of all OrderDetails to the key of the order. When you enter the loop orderDetail.OrderId is != 0 for all detail items and you always enter the branch which sets the state to Modified. No orderDetail item is in Added state anymore when the loop is finished.

ModelState.isValid error

I have created an Edit Action method but it is not going inside ModelState.isValid. How can I check the error?
public PartialViewResult UpdateAccountDetails(string accountNumber)
{
CreditReportService crService = new CreditReportService();
AccountInfo account = new AccountInfo();
account.Account = service.GetAccountDetails(accountNumber);
account.AccountStatuses = service.GetAccountStatuses();
account.AccountTypes = service.GetAccountTypes();
account.CreditTerms = service.GetCreditTerms();
return PartialView("_UpdateAccountDetails", account);
}
[HttpPost]
public ActionResult UpdateAccountDetails(Account account)
{
if (ModelState.IsValid)
{
service.SaveAccount(account);
TempData["message"] = "Account has been updated successfully!";
AccountInfo accountInfo = new AccountInfo();
accountInfo.AccountStatuses = service.GetAccountStatuses();
accountInfo.AccountTypes = service.GetAccountTypes();
accountInfo.CreditTerms = service.GetCreditTerms();
return PartialView("_UpdateAccountDetails", accountInfo);
}
else
{
return PartialView("_UpdateAccountDetails", account);
}
}
By accessing the ModelState.Errors collection. The collection contains a collection of ModelError items, which contain the error message and exception that was thrown to cause the model error.
Edit:
I guess I should have looked myself. It seems that the controller's ModelState is actually a (dictionary) collection of ModelState's. To get all the errors, you should be able to get all instances of the ModelError classes via:
var errors = ModelState.Select(x => x.Value.Errors).ToList();
var errors = var errors = ModelState.Where(m=>m.Value.Errors.Any()).Select(m => m.Value.Errors).ToList();
To only get a list of the errored fields only, rather than all fields and error list (exclude Errors length == 0).

Resources