Detect state change of model property, in controller, using Entity Framework
Solution 1
I believe you can compare the edited entity with the original one read from the database.
Something like:
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
var original = db.Find(project.ID);
bool changed = original.StartDate != project.StartDate || original.Duration != project.Duration;
if (changed)
{
original.StartDate = project.StartDate;
original.Duration = project.Duration;
doSomething();
db.Entry(original).CurrentValues.SetValues(project);
db.SaveChanges();
}
}
return View(project);
}
Solution 2
You can solve it by carrying old values via ViewBag.
In action:
public ActionResult Edit(int? id)
{
//...Your Code
ViewBag.OrigStartDate = project.StartDate;
ViewBag.OrigDuration = project.Duration;
return View(project);
}
Add hidden elements to View
...
@Html.Hidden("OrigStartDate", (DateTime)ViewBag.OrigStartDate)
@Html.Hidden("OrigDuration", (int)ViewBag.OrigDuration)
...
Add these parameters to the post method and check them for changes
[HttpPost]
public ActionResult Edit(DateTime OrigStartDate, int OrigDuration)
{
if (ModelState.IsValid)
{
if (OrigStartDate != project.StartDate || OrigDuration != project.Duration)
doSomething();
db.Entry(project).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.FileTypeId = new SelectList(db.FileTypes, "Id", "TypeName", dbinfo.FileTypeId);
return View(project);
}
Solution 3
Use .AsNoTracking().FirstOrDefault to retrieve the original object for comparison:
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
Project original = db.AsNoTracking().FirstOrDefault( p => p.ID == project.ID);
if (original.StartDate != project.StartDate || original.Duration != project.Duration)
doSomething();
db.Entry(project).State = EntityState.Modified;
db.SaveChanges();
}
return View(project);
}
Solution 4
If you want to do it without querying your persistance layer I guess adding the old values as fields in your model and then keep them in the page as hidden fields is the easiest way to solve this.
So add CurrentStartDate and CurrentDuration in your model:
public class Project {
public int ID { get; set; }
//... some more properties
public DateTime StartDate { get; set; }
public int Duration { get; set; }
public DateTime CurrentStartDate { get; set; }
public int CurrentDuration { get; set; }
}
and then add the hidden fields with the old values in your view:
@Html.HiddenFor(model => model.CurrentStartDate )
@Html.HiddenFor(model => model.CurrentDuration )
This will give you something to compare the selected values with in your controller action.
Related videos on Youtube
Chopo87
Updated on June 16, 2020Comments
-
Chopo87 almost 4 years
I have a more or less standard looking model:
public class Project { public int ID { get; set; } //... some more properties public DateTime StartDate { get; set; } public int Duration { get; set; } }
If the user modifies
StartDate
or projectDuration
, I have to call a function to update a simulation. In order to achieve this I'd like to detect the state change of the fieldsStartDate
andDuration
within a controller.Something like that:
if(project.StartDate.stateChange() || project.Duration.stateChange())
Here is an example of what the Controller Method would look like:
[HttpPost] public ActionResult Edit(Project project) { if (ModelState.IsValid) { if(project.StartDate.stateChange() || project.Duration.stateChange()) doSomething(); db.Entry(project).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(project); }
Any idea, how can I achieve this?
-
Chopo87 almost 11 yearsActually I would prefer to do all the checking within the controller if possible, keeps the code cleaner. Could you expand on how to
querying your persistence layer
, that sounds like what I'm trying to achieve. -
Per Kastman almost 11 yearsThen you have to fetch the current project from your db and check the old values and compare to the values you are getting from the client.
-
Chopo87 almost 11 yearsI alreadey tried this. I get the following error: ` An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.`
-
Chopo87 almost 11 yearsI already tried that: I get the following exception
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
-
Stefan Szasz almost 11 yearsWith or without 'db.Entry(original).State = EntityState.Modified;'?
-
Chopo87 almost 11 yearsOk, I stand corrected, that seems to work, however having to manualy assign
original.value= project.value;
can get a bit messy, this can be solved by replacingdb.Entry(original).State = EntityState.Modified;
withdb.Entry(original).CurrentValues.SetValues(project);
-
trejder almost 10 years@Chopo87 Please, respect other users of this site. If you find any kind of mistake in answer, please edit and correct it. Don't just leave "updates" in comments, because comments are second-class citizens here and may be easily missed. Always try to keep answers as accurate as possible. Thank you.
-
Shaiju T over 8 yearscan we use
auto mapper
with viewmodel and make it easy instead of writing multiple||
? -
Stefan Szasz over 8 years@stom, sure you can. Although you can achieve the same thing using a read-only property on the project entity that encapsulates those conditions if I understand correctly...
-
Shaiju T over 8 yearsi have done this by using
bool
changed and||
operator as you mentioned , thanks for that, so to make it clear actually i am getting shipping address details from user using a form generated withView Model
, i want to check if postedFormValues != existing shipping address details in database, if true then update modified shipping details in database, can we useauto mapper
? if yes, can you show an example or reference link ? -
Doctor Jones about 7 yearsI prefer this solution, as it does not involve an extra roundtrip to the database