I am having trouble saving my entities after updating them. I can add new entities like this: add(student); but if I tried this:
if (ModelState.IsValid)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("someView");
}
I get this error message:
System.Data.Entity.Infrastructure.DbUpdateConcurrencyException was unhandled by user code
Message=Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or
deleted since entities were loaded. Refresh ObjectStateManager
entries.
Here’s my controller method:
[HttpPost]
public ActionResult ClassAttendance(InstructorIndexData viewModel, FormCollection frmcol)
{
var instructorData = new InstructorIndexData();
string[] AllFstMNames = frmcol["item.Student.FirstMidName"].Split(',');
string[] AllLstNames = frmcol["item.Student.LastName"].Split(',');
string[] AllAddresses = frmcol["item.Student.Address"].Split(',');
string[] AllEnrollmentDates = frmcol["item.Student.EnrollmentDate"].Split(',');
//more of the same code…
var student = new Student();
var enrollment = new Enrollment();
for ( int i = 0; i < AllFstMNames.Count(); i++)
{
student.FirstMidName = AllFstMNames[i];
student.LastName = AllLstNames[i];
student.Address = AllAddresses[i];
student.EnrollmentDate = Convert.ToDateTime(AllEnrollmentDates[i]);
if (!string.IsNullOrEmpty(frmcol["item.Grade"]))
{
enrollment.Grade = Convert.ToInt32(AllGrades[i]);
}
enrollment.StudentID = Convert.ToInt32(AllStudentIds[i]);
enrollment.attendanceCode = Convert.ToInt32(AllAttendanceCodes[i]);
enrollment.classDays = AllclassDays[i];
enrollment.CourseID = Convert.ToInt32 (AllCourseIds[i]);
//update rows
}
if (ModelState.IsValid)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("someView");
}
Can you help me with just being able to update values in the database?
While I was looking at the code here, my initial thought is that it doesn't seem quite right to have a for loop that updates the student and enrollment objects multiple times and then to have only one call to db.SaveChanges outside the loop. This is concerning because only the last iteration of the for loop will be applied when the data is saved to the database. (You have a comment to "update rows" at the end of the for loop - perhaps some code is missing or misplaced?)
Then, I started thinking about why it would be necessary to manually set the Entry(...).State property. Wouldn't the db automatically know that an object is modified and needs to be saved? That lead me to this question: Where is db defined? What technology stack is being used there?
Finally, after making an assumption that the db object might work something like the MS LINQ-to-SQL feature, I noticed that the the student object is newly instantiated before the for loop. This is fine for inserting new data, but if you are wanting to update existing data, I believe you need to first get a copy of the object from the database and then update the properties. This allows the db object to monitor the changes (again, assuming that it has this capability). (If this is not the case, then it leads me to wonder how the db will know which record in the database to update since you are not setting anything that appears to be a primary key, such as StudentId, on the student object in the loop.)
Related
I think these are essentially the same method, but the first one queries the db first, so has less performance due to hitting the db twice. I will only have 40 users at most so performance isn't too big an issue. Is there any other reason to use one over the other?
Grab the entity from the db first, change it then save it:
public void UpdateStudent(StudentModel model)
{
using (var _db = new AppEntities())
{
Student student = new Student();
student = _db.Student.Find(model.studentId);
student.FirstName = model.FirstName;
student.LastName = model.LastName;
student.DOB = model.DOB;
student.GradeId = model.GradeId;
_db.Entry(student).State = System.Data.Entity.EntityState.Modified;
_db.SaveChanges();
}
}
Change the entity and let EF find it in the DB and update:
public void UpdateStudent(StudentModel model)
{
using (var _db = new AppEntities())
{
Student student = new Student()
{
student.StudentId = model.StudentId,
student.FirstName = model.FirstName,
student.LastName = model.LastName,
student.DOB = model.DOB,
student.GradeId = model.GradeId
};
_db.Entry(student).State = System.Data.Entity.EntityState.Modified;
_db.SaveChanges();
}
}
In first code snippet you take some version of entity form db. If other thread or proccess modifies the same entity I don't think EF would let you just do an update as your base version of entity differs from that one in db right before an update query.
In the second one if some thread or process modifies this entity while you're processing this request you probably could lose that change.
EDIT: I never tired that. I'm always getting the entity and then modify and save but you could write a test to verify what happens.
In your first snippet, you don't have to mark the entity as Modified, because the change tracker takes care of that. This is important to note because it also defines the difference between the two methods. I'll explain.
Let's assume that of all assignments (student.FirstName = model.FirstName; etc.) only the first one is a real change. If so -
The first code fragment (but without marking the entity as Modified) triggers an update statement that only updates FirstName.
The second code fragment always updates all fields in Student.
This means that the first fragment is less likely to cause concurrency conflicts (someone else may change LastName in the mean time and you don't overwrite this modification by stale data, as happens in the second scenario).
So it's about fine-grained changes vs. a sweeping update, roundtrips vs. redundancy:
the first scenario takes roundtrips but is more concurrency-safe.
the second scenario takes no roundtrips but is less concurrency-safe.
It's up to you to balance the trade-offs.
To make this choice a little bit harder, there is a third option:
public void UpdateStudent(StudentModel model)
{
using (var _db = new AppEntities())
{
Student student = new Student()
{
student.StudentId = model.StudentId,
student.FirstName = model.FirstName,
student.LastName = model.LastName,
student.DOB = model.DOB,
student.GradeId = model.GradeId
};
_db.Students.Attach(student);
_db.Entry(student).Property(s => s.FirstName).IsModified = true;
_db.Entry(student).Property(s => s.LastName).IsModified = true;
_db.Entry(student).Property(s => s.DOB).IsModified = true;
_db.Entry(student).Property(s => s.GradeId).IsModified = true;
_db.SaveChanges();
}
}
No roundtrip and now you only mark 4 properties as modified. So you still update too many properties if only one was actually changed, but four is better than all.
And there's more to this "rondtrips vs redundancy" question, but I explained that elswhere.
I am developing a Single Page App using Hot Towel Template, using breeze...and I have come across a peculiar problem, and I am unable to figure out the internal working which causes it...
I have a Programmes table, and the Programmes table has a foreign key to Responses, so the structure of Programmes is:
Id, ResponseID, Name and Date
and the Page has Name and Date, the foreign comes from RouteData.
and for one ResponseId in Programmes table, I want to save only on Programme.
So, when user comes to this page, it check the Programmes table that if it has an Entry for that particular Response Id, if yes, it goes in Edit case and if not it goes to Add a new entry case.
To achieve this, what I am doing is below:
var objTempProgramme = ko.observable();
var objProgramme = ko.observable();
function activate(routeData) {
responseId = parseInt(routeData.responseId);
// Create a Programme Entity
objProgramme(datacontext.createProgramme());
// Fill in a Temporary Observable with Programmes data
datacontext.getEntitiesDetailsByRelativeId('responseID', responseId , 'Programmes', objTempProgramme, true).then(function(){
// Check if Programmes Exists
if (objTempProgramme() != null && objTempProgramme() != undefined) {
// What I am doing here is filling my Programmes Entity with data coming from database (if it is there)
objProgramme(objTempProgramme());
} else {
// The Else Part assigns the Foreign Key (ResponseId) to my Entity Created above
objProgramme().responseID(responseId);
}
});
}
In datacontext.js:
var createProgramme = function () {
return manager.createEntity(entityNames.programme);
}
var getEntitiesDetailsByRelativeId = function (relativeIdName, relativeId, lookupEntity, observable, forceRefresh) {
var query = entityQuery.from(lookupEntity).where(relativeIdName, "==", relativeId);
return executeGetQuery(query, observable, forceRefresh);
};
Now when I call manager.saveChanes on my page, I would Expect objProgramme to be saved, in any case, be it edit or be it save,
but what breeze is doing here is that though it is filling objTempProgramme in objProgramme, but it is also leaving the entity objProgramme unreferenced with its manager, so that when I call save, it tries to save that entity too (2 entities in total, objProramme and one unreferenced one), but that entity does not have foreign key set and it fails..but my question is why?
Assigning one entity to another does not mean all its properties get assigned to another? And why is that unreferenced entity present?
Is there an equivalent of Rails ActiveRecord::Callbacks in ASP MVC?
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
I'm in a situation where we are not using identities for our primary key. We do this for reasons specific to our DB sharding design. Because of this we have a lookup table to find the next ID for a specific table. I'd like to automatically get this value and set it in an abstract class whenever a model is created/updated and before it is saved. I also need to update the lookup table with an incremented 'nextID' after the save is successful.
I'm open to other solutions on how to do this without callbacks as well.
So you need the callback just to increment ID in the lookup table? AFAIK there is no equivalent in ASP.NET, may be you could try with Async Controllers (http://msdn.microsoft.com/en-us/library/ee728598%28v=vs.100%29.aspx) and wait for a state change from the successful save, but I would prefer use a service specifically for this like Snowflake (https://github.com/twitter/snowflake/).
I found a solution using overrides as opposed to callbacks. It's my hope that ASP mvc adds support for callbacks as the framework continues to mature because callbacks allow for cleaner code by allowing the OnSave event to exist in the model[s] that the event is concerned with rather than the centralized DbContext class (separation of concerns).
Solution:
The SaveChanges method can be overridden in the Context Class (Entity Framework Power Tools creates the Context class is the 'Models' directory).
public override int SaveChanges()
{
// create a cache for id values in case their are multiple added entries in the dbcontext for the same entitytype
Dictionary<string, UniqueID> idCache = new Dictionary<string, UniqueID>();
IEnumerable<DbEntityEntry> changes = this.ChangeTracker.Entries();
foreach (var entry in changes)
{
//check if this is a new row (do nothing if its only a row update because there is no id change)
if (entry.State == System.Data.EntityState.Added)
{
//determine the table name and ID field (by convention)
string tableName = entry.Entity.GetType().Name;
string idField = entry.Entity.GetType().Name + "ID";
UniqueID id = null;
//if we've already looked this up, then use the cache
if (idCache.ContainsKey(tableName))
{
id = idCache[tableName];
}
//if we havn't looked this up before get it and add it to the cache
else
{
id = this.UniqueIDs.Find(tableName, idField);
//if it doesn't already exist in the lookup table create a new row
if (id == null)
{
id = new UniqueID(tableName, idField, 1);
// since this is a new entry add it
this.UniqueIDs.Add(id);
}
else
{
// set the state to modified
this.Entry(id).State = System.Data.EntityState.Modified;
}
}
entry.CurrentValues[tableName + "ID"] = id.NextID;
id.NextID = id.NextID + 1;
}
}
return base.SaveChanges();
}
When I update my model I get an error on a child relation which I also try to update.
My model, say Order has a releationship with OrderItem. In my view I have the details of the order together with an editortemplate for the orderitems. When I update the data the link to Order is null but the orderid is filled, so it should be able to link it, TryUpdateModel returns true, the save however fails with:
InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.]
My update method:
public ActionResult ChangeOrder(Order model)
{
var order = this.orderRepository.GetOrder(model.OrderId);
if (ModelState.IsValid)
{
var success = this.TryUpdateModel(order);
}
this.orderRepository.Save();
return this.View(order);
}
I tried all solutions I saw on SO and other sources, none succeeded.
I use .Net MVC 3, EF 4.3.1 together with DBContext.
There are a number of code smells here, which I'll try to be elegant with when correcting :)
I can only assume that "Order" is your EF entity? If so, I would highly recommend keeping it separate from the view by creating a view model for your form and copying the data in to it. Your view model should really only contain properties that your form will be using or manipulating.
I also presume orderRepository.GetOrder() is a data layer call that retrieves an order from a data store?
You are also declaring potentially unused variables. "var order =" will be loaded even if your model is invalid, and "var success =" is never used.
TryUpdateModel and UpdateModel aren't very robust for real-world programming. I'm not entirely convinced they should be there at all, if I'm honest. I generally use a more abstracted approach, such as the service / factory pattern. It's more work, but gives you a lot more control.
In your case, I would recommend the following pattern. There's minimal abstraction, but it still gives you more control than using TryUpdateModel / UpdateModel:
public ActionResult ChangeOrder(OrderViewModel model) {
if(ModelState.IsValid) {
// Retrieve original order
var order = orderRepository.GetOrder(model.OrderId);
// Update primitive properties
order.Property1 = model.Property1;
order.Property2 = model.Property2;
order.Property3 = model.Property3;
order.Property4 = model.Property4;
// Update collections manually
order.Collection1 = model.Collection1.Select(x => new Collection1Item {
Prop1 = x.Prop1,
Prop2 = x.Prop2
});
try {
// Save to repository
orderRepository.SaveOrder(order);
} catch (Exception ex) {
ModelState.AddModelError("", ex.Message);
return View(model);
}
return RedirectToAction("SuccessAction");
}
return View(model);
}
Not ideal, but it should serve you a bit better...
I refer you to this post, which is similar.
I assume that the user can perform the following actions in your view:
Modify order (header) data
Delete an existing order item
Modify order item data
Add a new order item
To do a correct update of the changed object graph (order + list of order items) you need to deal with all four cases. TryUpdateModel won't be able to perform a correct update of the object graph in the database.
I write the following code directly using a context. You can abstract the use of the context away into your repository. Make sure that you use the same context instance in every repository that is involved in the following code.
public ActionResult ChangeOrder(Order model)
{
if (ModelState.IsValid)
{
// load the order from DB INCLUDING the current order items in the DB
var orderInDB = context.Orders.Include(o => o.OrderItems)
.Single(o => o.OrderId == model.OrderId);
// (1) Update modified order header properties
context.Entry(orderInDB).CurrentValues.SetValues(model);
// (2) Delete the order items from the DB
// that have been removed in the view
foreach (var item in orderInDB.OrderItems.ToList())
{
if (!model.OrderItems.Any(oi => oi.OrderItemId == item.OrderItemId))
context.OrderItems.Remove(item);
// Omitting this call "Remove from context/DB" causes
// the exception you are having
}
foreach (var item in model.OrderItems)
{
var orderItem = orderInDB.OrderItems
.SingleOrDefault(oi => oi.OrderItemId == item.OrderItemId);
if (orderItem != null)
{
// (3) Existing order item: Update modified item properties
context.Entry(orderItem).CurrentValues.SetValues(item);
}
else
{
// (4) New order item: Add it
orderInDB.OrderItems.Add(item);
}
}
context.SaveChanges();
return RedirectToAction("Index"); // or some other view
}
return View(model);
}
I am using asp.net mvc for an application. I've taken some guidance from Rob Conery's series on the MVC storefront. I am using a very similar data access pattern to the one that he used in the storefront.
However, I have added a small difference to the pattern. Each class I have created in my model has a property called IsNew. The intention on this is to allow me to specify whether I should be inserting or updating in the database.
Here's some code:
In my controller:
OrderService orderService = new OrderService();
Order dbOrder = orderService.GetOrder(ID);
if (ModelState.IsValid)
{
dbOrder.SomeField1 = "Whatever1";
dbOrder.SomeField2 = "Whatever2";
dbOrder.DateModified = DateTime.Now;
dbOrder.IsNew = false;
orderService.SaveOrder(dbOrder);
}
And then in the SQLOrderRepository:
public void SaveOrder(Order order)
{
ORDER dbOrder = new ORDER();
dbOrder.O_ID = order.ID;
dbOrder.O_SomeField1 = order.SomeField1;
dbOrder.O_SomeField2 = order.SomeField2;
dbOrder.O_DateCreated = order.DateCreated;
dbOrder.O_DateModified = order.DateModified;
if (order.IsNew)
db.ORDERs.InsertOnSubmit(dbOrder);
db.SubmitChanges();
}
If I change the controller code so that the dbOrder.IsNew = true; then the code works, and the values are inserted correctly.
However, if I set the dbOrder.IsNew = false; then nothing happens...there are no errors - it just doesn't update the order.
I am using DebuggerWriter here: http://www.u2u.info/Blogs/Kris/Lists/Posts/Post.aspx?ID=11 to trace the SQL that is being generated, and as expected, when the IsNew value is true, the Insert SQL is generated and executed properly. However, when IsNew is set to false, there appears to be no SQL generated, so nothing is executed.
I've verified that the issue here (LINQ not updating on .SubmitChanges()) is not the problem.
Any help is appreciated.
In your SaveOrder method you are always creating a new ORDER object. You need to change this so that if order.IsNew is false, it retrieves the existing one from the DB and updates it instead.
public void SaveOrder(Order order)
{
ORDER dbOrder;
if (order.IsNew)
{
dbOrder = new ORDER();
dbOrder.O_ID = order.ID;
}
else
{
dbOrder = (from o in db.ORDERS where o.O_ID == order.ID select o).Single();
}
dbOrder.O_SomeField1 = order.SomeField1;
dbOrder.O_SomeField2 = order.SomeField2;
dbOrder.O_DateCreated = order.DateCreated;
dbOrder.O_DateModified = order.DateModified;
if (order.IsNew)
db.ORDERs.InsertOnSubmit(dbOrder);
db.SubmitChanges();
}
I think you have the problem that your entity is detached from your context.
You should try to attach your entity back to your context if you want to update. The downside of LINQtoSQL is that for the re-attachment you'll need the original state of the object when it was detached...
Another solution is to re-get your entity from the context and copy all the data from your entity in the parameter. This will do until you'll have more complex entities.
What tvanfosson said.
I would just like to add that I use logic where if Id equals default(0 or Empty if using guids), then I assume it is new. Otherwise if I have the id passed in, then I go get the existing object and update it.