MVC (MVC3) checking is a Model exists or has values - asp.net-mvc

I have created a working DropDownListFor which gets the data from a selectlist which is Model.IssueSocialSec and then setting the value coming from the database is Model.SocialDBValue
However, when I click a Edit link which with query a repository passing the Model back to the page, that works, but if I do a needed redirect route to the page and nothing is there to bind the Model, then the page fails. I'm going to try having it pass back an empty Model, but I figured I would post this as I always like to hear feedback on "best practices" and lessons learned.
#Html.DropDownListFor(m => m.SelectedSocial, new SelectList(Model.IssueSocialSec, "Value", "Text", Model.SocialDBValue), "")

It sounds like you just need to wrap the DropDownListFor in a <form> with a url pointing to an action that will allow you to edit. The form can use a GET request if it's an idempotent operation and you could use JavaScript to submit the form when the value of the <select> is changed, falling back to rendering a button for submission for when JavaScript is disabled.
Generally, I structure MVC controllers and actions as so
public class ProfilesController : Controller
{
public IProfileRepository Profiles { get; private set; }
public ProfilesController(IProfilesRepository profiles)
{
Profiles = profiles;
}
[HttpGet]
public ActionResult Index()
{
var profiles = Profiles.All();
return View(new ProfilesModel { Profiles = profiles });
}
[HttpGet]
public ActionResult Edit(int id)
{
var profile = Profiles.GetById(id);
return View(new ProfileModel { Profile = profile });
}
[HttpPost]
public ActionResult Edit(ProfileModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var profile = Profiles.GetById(id);
// update the profile
Mapper.Map(model, profile);
if (Profiles.Update(profile))
{
TempData["message"] = "Profile updated successfully";
}
return RedirectToAction("Edit");
}
}
Index will render all the profiles. Against each profile, an <a> will be rendered with a URL pointing to Edit and the URL will include the id for the profile to edit. Edit view will post a form to Edit and the profile will be updated with changes from the model.
I recommend looking at something like NerdDinner or MVC Music store to get an idea of how they structure their code.

I ended up fixing it like this:
ChildInfoModel childviewmodel = new ChildInfoModel();
return View(childviewmodel);
before I was trying to just do:
return View()

Related

Can I pass an alternative return link to an MVC controller action?

I have an MVC5 application which has several controllers, scaffolded with EF6 CRUD actions and associated views. One of these controller/view sets is used for managing a table of patient identifiers, and on completion of an edit or delete, the controller returns an action link to the identifiers index view, as expected.
However, the patient identifiers are also displayed on the various views of the patients controller, and from the Patient.Edit view I have Html.ActionLink calls to the identifier controller's edit or delete actions. When the latter are called from the Patient.Edit view, I would like them to return to that on completion.
Is there any way I can accomplish this?
Yes, but this is always a manual process. There's nothing built into MVC specifically for return URLs.
Essentially, your links to edit/delete will need to include a GET param, usually called returnUrl, though the name doesn't matter, which will be set to the current page URL. For example:
#Html.ActionLink("Edit", new { id = patient.Id, returnUrl = Request.RawUrl })
Then, your edit/delete GET action should accept this parameter, and set a ViewBag member:
public ActionResult Edit(int id, string returnUrl = null)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
In your edit form, add a hidden field:
#Html.Hidden("returnUrl", ViewBag.ReturnUrl)
In your POST edit action, again, accept the param:
[HttpPost]
public ActionResult Edit(int id, Patient model, string returnUrl = null)
But inside this action is where you'll do something different now. Typically, when you've got a successful post and have saved the object or whatever, you then do something like:
return RedirectToAction("Index");
However, instead, you should now check to see if returnUrl has a value, and if it does, redirect to that instead:
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index");
The MVC5 with Identity sample project has a nice helper method that it uses:
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
That would just go into your controller and basically does the same as I've already described with two notable differences:
It uses Url.IsLocalUrl to check that the return url is actually a URL on this site. That's a smart check, as since this is initially passed in the query string of the URL, it's open to be manipulated by a user.
It encapsulates the logic, so you don't have to remember how to this should be handled. When you have a successful POST, you simply return RedirectToLocal(returnUrl), and if there's a return URL set, it will be used. Otherwise, the fallback redirect will used.
This is how I did it in one of my projects:
public ActionResult Edit(int id, string returnUrl)
{
// find the model (code not shown)
return View(model);
}
In the Edit view you don't need to do anything special, in the Post Action you have
[HttpPost]
public ActionResult Edit(Model model)
{
if (ModelState.IsValid)
{
// save Model...
return Redirect(Request.Params["returnUrl"]);
// Request.Query will work as well since it is in the querystring
// of course you should check and validate it as well...
}
// else return the View as usual, not shown
}
To use it, when creating the "Edit" link from your pages you simply need to specify the extra returnUrl parameter:
#Html.ActionLink("Edit", "Edit",
new { controller = "YourController",
returnUrl = Url.Action("Index", "ThisController",)
})
Hope it helps.

What do I have to do to redirect on same page's controller after clicking on Action link

View
#Html.ActionLink("Edit","Name",new { id = item.Id })
controller
[HttpPost]
public ActionResult Action_Result_Name(Model_Name Obj_Model, string Add, int id)
{}
When I click on actionlink I want to redirect in Same page contoller.
But when I put ("int id") in actionResult form it won't go into controller for the first time
Your ActionLink will create is a HTTP GET and your controller's action is decorated with HTTPPOST attribute.
What you should be using is a PRG pattern: POST-REDIRECT-GET.
For example: If you have a page for list of products and an edit (GET) action to edit a single product. Your View should have a form that posts the edited product to a HTTP POST action and then within that action you redirect to a GET:
Product Controller Actions:
[HttpGet]
public ActionResult Get(int id)
{
//Get product by id
var productRepository = new ProductRepository();
var product = productRepository.GetProductById(id);
return View(product);
}
[HttpGet]
public ActionResult Edit(int id)
{
//Get product by id
var productRepository = new ProductRepository();
var product = productRepository.GetProductById(id);
return View(product);
}
[HttpPost]
public RedirectToActionResult Edit(Product product)
{
//validation checks should happen first
//if model state is valid then save product
var productRepository = new ProductRepository();
var productId = productRepository.Save(product);
return RedirectToAction("GetProduct", new {id = productId});
}
EditProduct View:
#model Product
<form action="/product/edit" method="POST">
#*
Place HTML to show your HTML edit controls
to allow users to edit Product values
*#
</form>
There are a lot of things that are basic here. You may want to make sure you use a DI framework like Ninject, StructureMap etc so that all your repositories. But it at least gives you a general idea.
Hope this helps.

MVC Routing Issue - refreshing same page

I have an Edit page and once the form is submitted I'm refreshing the page instead of redirecting the user to the Index page. To do so I'm saving the ID of the item in a temp variable and then use it to redirect the user to the edit page using the temp variable ID. Something like this:
[HttpGet]
public ActionResult Edit(Guid id)
{
TempData["CategoryID"] = id;
Category c = new CategoriesBL().GetCategory(id);
return View(c);
}
[HttpPost]
public ActionResult Edit(Category c)
{
new CategoriesBL().UpdateCategory(c);
return RedirectToAction("Edit", (Guid)TempData["CategoryID"]);
}
That's working fine. However I have two methods in a different form on the same page and whenever I submit either of these two methods the redirection is not working and I'm getting an exception.
One of the methods that's not working:
[HttpPost]
public ActionResult AddNewThumbnail()
{
List<byte[]> thumbs = new List<byte[]>();
for (int i = 0; i < Request.Files.Count; i++)
{
thumbs.Add(ConvertToByteArray(Request.Files[i].InputStream));
}
new CategoriesBL().AddCategoryThumbnail(thumbs, (Guid)TempData["CategoryID"]);
return RedirectToAction("Edit", (Guid)TempData["CategoryID"]);
}
Exception:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Guid'....
I think it's an issue with routing but the fact is that the same implementation is used and it's working on one form and not the other. I'm not sure whether I'm doing something wrong or if there's any better way to do this.
Note: I have debugged the code several times and the ID I'm passing to the method does have a value in it. However when the page reloads the URL has no ID present.
Debugging
The problem seems to be due to the different forms I'm using. The first form I'm just editing text and it is like so:
#using (Html.BeginForm()) {
// ....
}
In the second form I'm saving and uploading images so the form has to be different
#using (Html.BeginForm("AddNewThumbnail", "Category", FormMethod.Post, new { enctype = "multipart/form-data" })) {
// ....
}
Somehow when I changed the form to the 'normal' one everything worked. But of course I can't use it as I want to save images from this form.
pass the value from your view. Something like this
[HttpPost]
public ActionResult Edit(Category c, FormCollection f)
{
Guid categoryID = (Guid)f["catergoryID"];
new CategoriesBL().UpdateCategory(c);
return RedirectToAction("Edit", catergoryID);
}
In your first example, you have initialisation:
TempData["CategoryID"] = id;
in GET method. So, you have to init your (Guid)TempData["CategoryID"] before you try to access it here:
return RedirectToAction("Edit", (Guid)TempData["CategoryID"]);

Model values no shown updated in the view after a POST

I have the following simple (for the purpose of the question) controller/action:
public ActionResult Edit(int id)
{
User user = repository.GetUser(id);
return View(user);
}
[HttpPost]
public ActionResult Edit(User user)
{
user.Name = user.Name.ToUpper();
return View(user);
}
and the view was generated as Edit for User using regular steps in VS
The problem I'm facing is that whatever has been entered in the Name text-box will be showed back to the user but without uppercase. I have checked and the Edit action for POST is executed and user.Name value become UPPER CASE but .TextBoxFor is still using the value entered by the user which is in lower case.
I have checked also that if I print (without the use of any Html Helper) the value of Model.Name after the POST, it will be in UPPER CASE as I would like the Html.TextBoxFor to behave.
Is that a bug of MVC?
Is there a way of making this work in the way I want, meaning that the action/controller is the piece in the puzzle to decide data values for the View and not the view using the ones in the Request object and ignoring the changes that the controller might have done?
Thanks in advance
Use RedirectToAction and pass the Id for the user, it's not a bug, is the POST Behaviour
Update: You need to persist the information before RedirectToAction as Dismissile said, or use TempData if you do not want to persist.
[HttpPost]
public ActionResult Edit(User user)
{
user.Name = user.Name.ToUpper();
return RedirectToAction("Edit", new { id = user.id });
}
You can use ModelState.Remove(nameOfProperty) like:
[HttpPost]
public ActionResult Edit(User user)
{
ModelState.Remove("Name");
user.Name = user.Name.ToUpper();
return View(user);
}
It's will work.

asp.net-mvc / linq to sql - do i always need an HTML.TextBox to do a Edit Save?

I have a UserController and an Edit.aspx. There is one field that is my primary key so i dont want to allow users to edit this field.
The issue is that if i remove the
<%= Html.TextBox("Email", Model.Email) %>
then when the asp.net-mvc magic calls my Controller code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, tblMailingList user_)
{
try
{
repo.UpdateUser(user_);
return RedirectToAction("Index");
}
catch
{
return View();
the email field of the tblMailingList is null. The issue is that i need this as the lookup in the table to retrieve the current record and obviously if its null i get an exception.
When i put the textbox back for this field, it works fine. It seems crazy that i would have to have a textbox and allow editing to pass this field over to the controller. i tried putting it in a label and it still shows up as null in the controller.
any suggestions?
My first question would be why are you doing the lookup on the Email field and not the Id field?
You can pass parameters in your Form declaration to be passed through to your Controller.
<% using (Html.BeginForm(
"MethodName", "Controller", FormMethod.Post,
new { id = Model.Id, email = Model.Email)) { %>
I'm not sure if I got the method declaration correct so please check.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, string email, tblMailingList user_)
{
try
{
repo.UpdateUser(user_);
return RedirectToAction("Index");
}
catch
{
return View();
I would recommend updating slightly differently as your tblMailingList user will not be valid to be updated in your Repository.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection form)
{
tblMailingList user = repo.GetUser(id); // get the user using the id
// so we can update in the same
// context
UpdateModel(user); // this will automatically update
// your user model with values
// from the form
try
{
repo.UpdateUser(user);
return RedirectToAction("Index");
}
catch
{
return View();
If you just want a field that could passed to controller which needs to be invisible in the form, Html.HiddenField could work for your case.
Do I get wrong?

Resources