Following is the default code in mvc, what I want is go give 'hint' a value in this function to let me known the data has changed
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,categoryID,subTitle,subject,sen,image,hint")] List list)
{
if (ModelState.IsValid)
{
db.Entry(list).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("ListSpryList", new { id = list.categoryID });
}
return View(list);
}
I think I maybe should insert something like following, before the code "db.saveChanges()"
db.Engtry(list.hit).value="changed";
it is obvious wrong but something like this.
Oh, sorry everyone, it is a quite stupid question, only need to insert a sinple code
list.sen = "changed";
before
db.SaveChanges();
then I got what I want
Sorry everyone for waste your time.
Related
Hope someone can help me with this.
I have a controller in my ASP.NET MVC project that is used for editing a so called survey form. The survey form has a 1-to-1 relation with objects of type Form. When saving the survey form I also want to set the name of the form. However the form property is null. The FormID property has the correct value (using database first, EF5). Here is the problem.
[HttpPost]
public ActionResult Edit(SurveyForm surveyform)
{
if (ModelState.IsValid)
{
db.Entry(surveyform).State = EntityState.Modified;
// This cannot be done because surveyform.Form is null
surveyform.Form.Name = "Wish this would work!";
db.SaveChanges();
}
}
My question is: how can I 'attach' surveyform to the model so that is loads related data?
Thanks in advance!
At a guess, I'd say the entity being POSTed back isn't being tracked, so Entity Framework doesn't realise it's from the database. The line db.Entry(surveyForm) gets the entry matching the POSTed form, but you're not retaining it. Try this:
[HttpPost]
public ActionResult Edit(SurveyForm surveyform)
{
if (ModelState.IsValid)
{
var formEntry = db.Entry(surveyform);
formEntry.State = EntityState.Modified;
formEntry.Form.Name = "This might work...";
db.SaveChanges();
}
}
Hopefully, the .Entry() will get you the database's version. Unfortunately, you'll probably have to copy the property values accross, although you might find you can use .Attach() and then copy over the navigation properties from the database's version.
Having said that, it's generally a good idea to not use your database models in the view if you can help it; separation of concerns and all that. If there's more than a couple of properties on that model that are needed for the database but not the view (or vice versa) then you might want to use a local view model for it and just copy the properties to a freshly-retrieved database entity.
Try:
db.Entry(surveyform).Reference(x => x.Form).Load();
surveyform.Form = db.Entry(surveyform).Reference(x => x.Form).CurrentValue;
In your model, make sure you have a navigation propery like this:
public virtual Form Form { get; set; }
And then in your DbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<SurveyForm>().HasOptional(p => p.Form);
}
HasOption can be replaced with HasRequired.
I have never had the exact issue you are having, so I hope this helps.
Eventually I combined multiple suggestions above and ended up with the following.
I added the following to my view:
#Html.TextBox("formName", Model.Form.Name)
and then changed the code in my controller to:
[HttpPost]
public ActionResult Edit(string formName, SurveyForm surveyForm)
{
if (ModelState.IsValid)
{
var surveyFormEntry = db.Entry<SurveyForm>(surveyForm);
db.SurveyForms.Attach(surveyForm);
surveyFormEntry.State = EntityState.Modified;
surveyFormEntry.Reference(x => x.Form).Load();
surveyForm.Form = db.Entry(surveyForm).Reference(x => x.Form).CurrentValue;
surveyForm.Form.Name = formName;
db.SaveChanges();
return RedirectToAction("Index", new { surveyId = surveyForm.SurveyID });
}
It was a lot of trial and error. A way of programming I so disapprove on and dislike but I'm happy it finally works.
Thanks all!
Need to compare the original value with the current value in order to do some updates on objects prior to save changes. How to get the original value of an object? The following does not work:
public ActionResult Edit(int id = 0)
{
Project project = db.Projects.Find(id);
...
db.Projects.Attach(project); // ATTACH here
...
}
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
db.Entry(project).State = EntityState.Modified; // The STATE was DETACHED here...
...
}
Is this a configuration issue, or did I miss something? Thanks in advance for any help!
The problem with your solution is the lifetime of the controller. By default a new instance of the controller is generated for each client request. This means that you need to load the original value inside the POST method of edit, because you cannot share the instance of your context.
I have edited the code sample after #Gerard's comment
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
var original_value = db.Projects.Find(project.ProjectId);
or
var original_value_detached = db.Projects.AsNoTracking().Where(P => P.ProjectId == project.ProjectId).FirstOrDefault();
}
}
Or you write your own implementation of the controller factory to share you context between two requests, but you should consider requests from different clients. You can find an example here
It looks like you are implementing concurrency. May be, you can try to catch your changes using DbUpdateConcurrencyException. Something like this:
try
{
...
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var dbValue = (Project)entry.GetDatabaseValues().ToObject();
if(dbValue.State == EntityState.Modified)
{
//***your code
}
}
I would really appreciate some insight on this: The following code
[HttpPost]
public ActionResult Edit(BeamCollection beamcollection)
{
if (ModelState.IsValid)
{
beamcollection.BeamMaterial = db.Types.Find(Convert.ToInt32(Request.Form.Get("BeamMaterial_ID")));
db.Entry(beamcollection).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Details", "Bridge");
}
return View(beamcollection);
}
When I attempt to modify a BeamCollection record, all changes are reflected and saved to the DB except for the beamcollection.BeamMaterial which takes the selected value from a DropDownList. When I debug, I can see that the selected value is being assigned to beamcollection.BeamMaterial!
By the way, this field is defined as follows
public virtual AllTypes BeamMaterial { get; set; }
So it reflects a one to many relationship with AllTypes, but it is a unidirectional relationship.
What is kind of strange (to me), is the the same technique is used for the Create action and it perfectly works:
public ActionResult Create(BeamCollection beamcollection)
{
if (ModelState.IsValid)
{
beamcollection.BridgeInfo = db.Bridges.Find(bridgeID);
beamcollection.BeamMaterial = db.Types.Find(Convert.ToInt32(Request.Form.Get("BeamMaterial_ID")));
db.BeamCollections.Add(beamcollection);
db.SaveChanges();
return RedirectToAction("Details", "Bridge");
}
return View(beamcollection);
}
Why is this happening and how to make it work, Please help.
try to use:
db.attach(beamcollection.GetType().Name,beamcollection);
db.ObjectStateManager.ChangeObjectState(beamcollection, EntityState.Modified);
db.SaveChanges();
Thanks to Fabrice's tip I managed to find the correct way, Here is the code:
var currentBeamCollection = db.BeamCollections.Find(beamcollection.ID);
db.Entry(currentBeamCollection).CurrentValues.SetValues(beamcollection);
currentBeamCollection.BeamMaterial = beamcollection.BeamMaterial;
db.SaveChanges();
The logic is as follows: get the original record, update all fields (except for the navigation properties, read below), update the navigation property, finally save.
When I tried to do the following
db.Entry(currentBeamCollection).CurrentValues.SetValues(beamcollection.BeamMaterial);
The system failed with an exception that complains about setting the ID property. I also read that CurrentValues.SetValues() doesn't update navigation properties, and I noticed that the BeamMaterial property was not being updated, so I needed to update it manually.
Thanks Fabrice.
I have following code that is accepting a form submission
[ActionName("TestingTemp"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult TestingTemp(FormCollection result)
{
string cat = "";
return View("Try");
}
Now the problem is even though it seems to load "Try" page, things break on the page because it doesn't fire the following code (which does get properly fired if I directly go to Try page).
public ActionResult Try()
{
ViewData["Test"] = DataLayer.Test(0, 10);
return View();
}
Also the url contains TestingTemp where it should contain Try, if you know what I mean.
I think what you are looking for is RedirectToAction. It will redirect to your other method and rewrite the URL.
[ActionName("TestingTemp"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult TestingTemp(FormCollection result)
{
string cat = "";
return RedirectToAction("Try");
}
the preferred way is to use redirectToAction but if u do want to go that way then u have to put the required data that u r doing in Try method like
[ActionName("TestingTemp"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult TestingTemp(FormCollection result)
{
string cat = "";
ViewData["Test"] = DataLayer.Test(0, 10);
return View("Try");
}
but as i said this way is not preferred i.e repeating ur code in each action rather u can just write something like
[ActionName("TestingTemp"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult TestingTemp(FormCollection result)
{
string cat = "";
return RedirectToAction("Try");
}
I have a problem with posting a multiselect, I get error:
"There is no ViewData item with the key 'NotificationUsergroups' of type 'IEnumerable'."
In the controller I have:
MultiSelectList NotificationUsergroups = new MultiSelectList(Usergroups, "UsergroupID", "UsergroupName", selectedNotificationUsergroupIDs);
ViewData["NotificationUsergroups"] = NotificationUsergroups;
In the view I have:
<%= Html.ListBox("NotificationUsergroups")%>
And in the post action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ObjectEdit([BindAttribute(Include = "BookingObjectID,BookingObjectName,Activated,ActivationStartDate,ActivationEndDate,AvalibleObjects,AvalibleObjectsPerBooking")]BookingObject bookingobject, int[] Objectcategories, int[] NotificationUsergroups, int[] CancellationUsergroups)
{
if (ModelState.IsValid)
{
try
{
_bs.SaveBookingObject(bookingobject);
if (NotificationUsergroups != null)
_bs.SaveNotificationUsergroups(bookingobject.BookingObjectID, NotificationUsergroups);
return View("CreateObject", new BookingObjectsAdminEditViewModel { BookingObject = bookingobject });
}
catch {
ModelState.AddModelError("SomeError", "errrrrrrrrror");
}
}
What might be wrong? I've checked the spelling and all, works if I dont run with the multiselect listing.
What makes the data "disappear"?
Thanks in advance
/M
You will need to set the viewdata in the POST accepting method as well because if it has an error it will return to the original view.
Kindness,
Dan
Or include them in your model :) You would need to alter view tho.
Your action must setup your ViewData.
If your action calls
ViewData["NotificationUsergroups"] = NotificationUsergroups;
everything should be alright.