Im having problem that it seems my model not updated after database change. I have a list that each have button to approve or reject.
using (Html.BeginForm("LeaveProcess", "Admin", FormMethod.Post, new { #class = "form-horizontal" }))
{
<input type="text" name="LvAppId" hidden value="#item.LvAppId">
<li><button type="submit" class="btn btn-link" name="AppVal" value=true data-confirm="are u sure?" >Approve</button></li>
<li><button type="submit" class="btn btn-link" name="AppVal" value=false >Reject</button></li>
}
When i press approve or reject button, the value of that submit button is go to controller
[HttpPost]
public ActionResult LeaveProcess(string LvAppId, bool AppVal)
{
DataChange.UpdateStatusFromAdmin(LvAppId, AppVal);
TempData["AlertMessage"] = "Your Action Has Been Processed";
return RedirectToAction("Index");
}
and update database column to "approved" or "reject".
public static void UpdateStatusFromAdmin( string LvAppId, bool AppVal)
{
if (AppVal == true)
{
db.Database.ExecuteSqlCommand("UPDATE LeaveApps SET Status='Approved' WHERE LvAppId={0}", LvAppId);
}
else
{
db.Database.ExecuteSqlCommand("UPDATE LeaveApps SET Status='Rejected' WHERE LvAppId={0}", LvAppId);
}
}
The database is updated (checked it with ssms), but when i reload the page, the value still not updated and i must restart debugging in visual studio to make it updated, what should i do? Thanks
If you are confident that that the query is correct and you can see changes being made to the backend using ssms but the changes aren't being reflected in your app.. I think that is a good indication that there is a problem with the context.
From your code it looks like you aren't disposing of the context properly.
Wrapping your data access inside a using statement like so:
using(var db = new DatabaseContext())
{
// perform data access here
}
Will automatically dispose of it...
See https://msdn.microsoft.com/en-gb/data/jj729737.aspx
If that doesn't solve your problem I'd then focus on your query
Besides writing your values to the database, you do nothing with them.
Create an object of your model and call your view.
var model = new YourModel
{
LvAppId = lvAppId,
AppVal = appVal
};
return View("Index", model);
Otherwise you have to make a call to the database to get your most recent values
Related
[HttpPost]
public ActionResult Index(DebuggerIndexFormHolder holder)
{
var result = _debuggerIndexFormProcessor.Process(holder.DebuggerIndexForm, ModelState);
if (result.IsSuccess)
{
return RedirectToAction("Item", new { id = result.SessionId });
}
}
[HttpGet]
public ActionResult Item(string id, string inputId)
{
var model = _debuggerSessionViewModelBuilder.Build(id, inputId);
return View(model); // it opens the required view in same window
}
HTML Code
<button id="submit" type="submit" data-loading-text="Loading..." class="btn btn-primary">Debug it!</button>
ActionResult "Index" is called and which in turns calls ActionResult "Item" , passing session ID to action result "Item", which fills model on session id basis then it calls View , I need to open this view in different window using same session id.
Can anyone please help me ?
Thanks
That's not something you can control from the server side code. You need to add aformtarget="_blank" attribute to your button.
<button id="submit" type="submit" formtarget="_blank" data-loading-text="Loading..." class="btn btn-primary">Debug it!</button>
Whether that opens a new tab or a new window is dependent on the user's browser settings.
I'm new to MVC and still haven't found a way to do this.
In the view I have this Clone button:
<a href="#Url.Action("Clone", "Game", new { pModelId = Model.Id })" class="btn btn-default btn-xs">
<i class="fa fa-plus"></i>Clone Game</a>
And in my Controller this:
[HttpPost]
public ActionResult Clone(int pGameId)
{
int lClonedGameId = mGameRepository.CloneGame(pGameId);
return RedirectToAction("Show", new { id = lClonedGameId, message = "Your game was cloned succesfully" });
}
I'm getting a 404 due to the [HttpPost] but I don't want to make it [HttpGet] since it writes to the DB. Is there a way to make that button go to the HttpPost method without a BeginForm or something like that?
If this is only a matter of having link instead of button to submit the form I suggest you adding a form and using javascript to submit a form when the link is pressed (below code assumes you are using jQuery):
#using (Html.BeginForm("Clone", "Game", FormMethod.Post, new {id = "form1"}))
{
//Your other elements go here
CloneGame
}
And hook the script:
$(function() {
$("a.cloneGameSubmit").click(function () {
$("#form1").submit();
});
});
I had asked question yesterday, which can be found here and
based upon the answer I got from asp.net/mvc forum which can be found here, I was told to clear my modelstate, as by default my form tends to hold its default value, and not the value I just tried to update. So, I added Modelstate.Clear(), which still doesn't work. Can anyone tell me if i'm using the ModelState.Clear() in a wrong place or if I have to change something?
So, here is the problem, I have a edit form which shows its current values in textboxes, when user clicks edit button. If a user wants to edit some current value which is shown in textbox he edits the value in text box and clicks the save changes button. What currently is happening is in my HttpPost method when i check the values that are being passed, I don't get the new value user just provided, rather I get the value that was shown as current value in form.
But when I check in the developer tools in chrome, it shows the new value user just provided as the value that is being passed to server.
Here is my view
#using BootstrapSupport
#model AdminPortal.Areas.Hardware.Models.EditModule
#{
ViewBag.Title = "Edit";
Layout = "~/Views/shared/_BootstrapLayout.basic.cshtml";
}
<fieldset>
<legend>Module <small>Edit</small></legend>
#using (Html.BeginForm("Edit", "Module"))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(m=>m.Id)
for(var i = 0; i < Model.Properties.Count(); i++)
{
#Html.HiddenFor(model=>model.HiddenProperties[i].Name)
#Html.HiddenFor(model=>model.HiddenProperties[i].Value)
<label class="label">#Model.Properties[i].Name</label>
<div class="input-block-level">#Html.TextBoxFor(model => model.Properties[i].Value)</div>
}
<div class="form-actions">
<button type="submit" class="btn btn-primary" id="Submit">Save changes</button>
#Html.ActionLink("Cancel", "ModuleList", null, new { #class = "btn " })
</div>
}
</fieldset>
<p>
#Html.ActionLink("Back to List", "ModuleList")
</p>
Here is the get and post method in controller
[HttpGet]
public ActionResult Edit(long id)
{
var module = _repository.GetModuleProperties(id);
ModelState.Clear();
return View(module);
}
[HttpPost]
public ActionResult Edit(EditModule module)
{
ModelState.Clear();
if (ModelState.IsValid)
{
_repository.SaveModuleEdits(module);
Information("Module was successfully edited!");
return RedirectToAction("ModuleList", "Module", new {area = "Hardware"});
}
Error("Edit was unsuccessful, if the problem persists please contact Merijn!");
return RedirectToAction("ModuleList", "Module", new { area = "Hardware" });
}
The problem is with your model:
public class EditModule
{
public long Id { get; set; }
public List<PropertyViewModel> Properties { get; set; }
public List<PropertyViewModel> HiddenProperties
{
get { return Properties; }
set { Properties = value; }
}
}
You're posting back both Properties and HiddenProperties, but only changing Properties. The modelbinder sets the new values in Properties and then sets the values for HiddenProperties which in turn sets Properties and you've just overwritten your changes.
I'm not sure what exactly you're trying to do with HiddenProperties, but it's completely broken as it's currently set up.
UPDATE: Suggested changes
Model
public class EditModule
{
public long Id { get; set; }
public List<PropertyViewModel> Properties { get; set; }
}
Removed HiddenProperties property
Controller Action
[HttpPost]
public ActionResult Edit(long id, EditModule module)
{
var originalModule = _repository.GetModuleProperties(id);
// do whatever comparisons you want here with originalModule.Properties / module.Properties
if (ModelState.IsValid)
{
_repository.SaveModuleEdits(module);
Information("Module was successfully edited!");
return RedirectToAction("ModuleList", "Module", new {area = "Hardware"});
}
Error("Edit was unsuccessful, if the problem persists please contact Merijn!");
return RedirectToAction("ModuleList", "Module", new { area = "Hardware" });
}
Edit POST version takes the id just like the GET version. You use this id to lookup the original version of the module from the database and then you can compare original and posted versions of Properties.
View
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
#Html.HiddenFor(m=>m.Id)
for(var i = 0; i < Model.Properties.Count(); i++)
{
<label class="label">#Model.Properties[i].Name</label>
<div class="input-block-level">#Html.TextBoxFor(model => model.Properties[i].Value)</div>
}
<div class="form-actions">
<button type="submit" class="btn btn-primary" id="Submit">Save changes</button>
#Html.ActionLink("Cancel", "ModuleList", null, new { #class = "btn " })
</div>
}
The Html.BeginForm() syntax tells Razor to just use the current page's URL as the action for the form. HiddenProperties form fields have been removed.
I have an edit form which has a label and current values in textbox, I want to check if the values in the form has been changed when the form is submitted.
Here is the form
<fieldset>
<legend>Module <small>Edit</small></legend>
#using (Html.BeginForm("Edit", "Module"))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(m=>m.Id)
for(var i = 0; i < Model.Properties.Count(); i++)
{
<label class="label">#Model.Properties[i].Name</label>
<div class="input-block-level">#Html.TextBoxFor(model => Model.Properties[i].Value, new { #value = Model.Properties[i].Value })</div>
}
<div class="form-actions" id="buttons">
<button type="submit" class="btn btn-primary" id="Submit">Save changes</button>
#Html.ActionLink("Cancel", "ModuleList", null, new { #class = "btn " })
</div>
}
</fieldset>
this results to
How can i check if the form has been changed? My httppost method of controller currently look like this
[HttpPost]
public ActionResult Edit(EditModule module)
{
if (ModelState.IsValid)
{
_repository.SaveModuleEdits(module);
Information("Module was successfully edited!");
return RedirectToAction("ModuleList", "Module", new {area = "Hardware"});
}
Error("Edit was unsuccessful, if the problem persists please contact admin!");
return RedirectToAction("ModuleList", "Module", new { area = "Hardware" });
}
}
It is fairly straight forward on the client side if you using something like Knockout. Here is an article that describes how to use Knockout for change tracking. This article uses a Knockout add-on called KoLite to make it even simpler.
One way to check if a value has changed from its original state (server side), is through HMAC mechanism.
Basically it generates a hash based on a string and secret key, and this hash is sent along with the form as a hidden field (http get), if the value is changed by the customer then the recalculation of the hash (http post) will be different from what is stored in the hidden field, then you know that someone change the value of that field.
This may be a little overworked but is one of the safest methods.
https://security.stackexchange.com/questions/20129/how-when-do-i-use-hmac
How to generate HMAC-SHA1 in C#?
I am very confused as to why this started happening as I had fixed the problem already. When this problem first occurred I did not have #Html.HiddenFor(model => model.OrganizationID) being passed through the POST action of the form. After I put that in - it worked just fine.
Now, it is not working again. The DbUpdateConcurrencyException is being thrown when I attempt to delete something. My Edit View works just fine.
I followed this tutorial to create a Model first approach.
These are the delete actions in my controller, OrganizationController:
public ActionResult Delete(int id)
{
using (var db = new VAGTCEntities())
{
return View(db.Organizations.Find(id));
}
}
//
// POST: /Organization/Delete/5
[HttpPost]
public ActionResult Delete(int id, Organization organization)
{
try
{
// TODO: Add delete logic here
using (var db = new VAGTCEntities())
{
db.Entry(organization).State = System.Data.EntityState.Deleted;
db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
This is my delete view:
#model VAGTC.Models.Organization
#{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<fieldset>
<legend>Organization</legend>
<div class="display-label">
#Html.DisplayNameFor(model => model.Name)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.Name)
</div>
</fieldset>
#using (Html.BeginForm()) {
#Html.HiddenFor(model => model.OrganizationID)
<p>
<input type="submit" value="Delete" /> |
#Html.ActionLink("Back to List", "Index")
</p>
}
I debugged it to see if the id is being passed and it is indeed being passed through to the POST action. I am unsure of where to go from here. As anything I search for brings up just adding the HiddenFor statement.
This exception indicates that for some reason, no rows were affected by your query (ie. the row was not deleted). Instead of setting the State of the entry, try using the following approach:
db.Remove(organization);
db.SaveChanges();
Edited in response to comments
When you post back the organisation object, it will be populated by the fields contained in the form element of your view. In your case that is only OrganisationId. You change the State of the entity object posted to the controller and save it. If you have set the Concurrency Mode property on any of your EF entities (in the .edmx for example), EF will throw a concurrency exception if the value you pass for that field does not agree with the database value. That could be why it needs the value of the Name field.
A better approach (if you're not concerned about concurrency during deletes) would be to remove the object in the method signature, and rename the Id argument to recieve the OrganisationId. Then read the object directly from the database before setting its state and saving it.