ActionResult HttpPost need to access value - asp.net-mvc-3

I am using MVC C#
Say I have the following ActionResult:
public ActionResult Create(string location)
{
...
View()
}
I need to use location primary in the [httppost]
[HttpPost]
public ActionResult Create(Employee employee)
{
...
// I need to access the value of location here but I dont' have access to the View
}
What is the best way of getting the value of location. I can create a viewmodel and pass that value to the View and then retreive it in the [HttpPost ]but I do not have access to the View as it is restricted.

There is a number of methods to pass data between controller methods in mvc. One of them is by using TempData.
You can save location in your GET-method
public ActionResult Create(string location)
{
TempData["location"] = location;
...
View()
}
and then retrieve it in your POST-method
[HttpPost]
public ActionResult Create(Employee employee)
{
var location = TempData["location"];
...
}
Although, using a viewmodel would be more preferrable.

Related

asp.net mvc 3 pass parameters

I have controller which gets list of keys and i want to pass them to another controller action:
public class DocumentController : Controller
{
private List<DocumentKey> _keys = new List<DocumentKey>();
[HttpPost]
public ActionResult Send(Document doc, IEnumerable<HttpPostedFileBase> file)
{
...
_keys = getKeys();
return RedirectToAction("Status", "Home", _keys);
}
i try to use
return RedirectToAction("Status", "Home", _keys);
and
return RedirectToAction("Status", "Home", new {keys = _keys});
But in Status keys is always null or has count = 0.
public class HomeController : Controller
{
public ActionResult Status(List<DocumentKey> keys)
{
return View(keys);
}
I can pass simple data, for example:
return RedirectToAction("Status", "Home", new {key = _keys.First().ToString()});
public ActionResult Status(string key)
this works, but is there a way to pass collection ?
Passing this type of data as query string parameters will not work as you are attempting to do so. If you need data to persist until your next request, it may be feasible to push the "keys" into tempdata.
public class DocumentController : Controller
{
private List<DocumentKey> _keys = new List<DocumentKey>();
[HttpPost]
public ActionResult Send(Document doc, IEnumerable<HttpPostedFileBase> file)
{
...
TempData["_keys"] = getKeys();
return RedirectToAction("Status", "Home");
}
public class HomeController : Controller
{
public ActionResult Status()
{
List<DocumentKey> keys = TempData["_keys"];
return View(keys);
}
RedirectToAction does a round trip by sending a HTTP 302 to the client. Is that really what you need?
Because if you don't, then the easiest would be to call your HomeController's Status method directly. You would also need to replace your call to View to explicitly specify the name of the view, i.e:
public ActionResult Status(List<DocumentKey> keys)
{
return View("Status", keys);
}
If you do need the round trip, then a solution would be to use TempData to store your data as suggested by Jesse.

Modifying an entity object gets saved to database but retrieving with .net doesn't reflect changes

I'm using an entity object called User. This is the function I use to save (in the repository):
public void saveUser(User user){
if (user.EntityState == System.Data.EntityState.Detached)
this._db.Users.Attach(user); // attach to the context
this._db.ObjectStateManager.ChangeObjectState(user, System.Data.EntityState.Modified);
this._db.SaveChanges();
}
This entity was created with the entity data model designer. This is changed by the view (I'm using the entity object as a model) and the save call is made by my controller.
When I edit the user, the changes get saved to the database, but the view sees the old properties. When I restart the program, the correct property values show up.
This is how I'm retrieving the object from the repository:
public IQueryable<User> GetUsers(String user_name)
{
IQueryable<User> userquery = from u in _db.Users
where u.user_name == user_name
select u ;
return userquery;
}
Controller:
public ActionResult ManageUser(String user_name)
{
IQueryable<User> users = this.users_db.getUsers(user_name);
User user = users.First();
return View(user);
}
[HttpPost]
public ActionResult ManageUser(User user){
this.users_db.saveUser(model.user);
ViewBag.message="Success";
return View(user);
}
I left out some of the exception and error checking code for brevity.
_db in GetUsers is probably an instance of ObjectContext? When are you instantiating it?
The behavior you are describing could be explained if you are keeping the same instance between requests. In this case it is returning the User objects that were already retrieved before you have updated the values in database. Refreshing them should help:
_db.Refresh(RefreshMode.StoreWins, user);
Though a better practice would be to create a new ObjectContext instance for each request.
[HttpPost]
public ActionResult ManageUser(User user){
this.users_db.saveUser(model.user);
ViewBag.message="Success";
return RedirectToAction("ManageUser");
}
or you can query the updated model again
[HttpPost]
public ActionResult ManageUser(User user){
this.users_db.saveUser(model.user);
ViewBag.message="Success";
IQueryable<User> users = this.users_db.getUsers(user_name);
user = users.First();
return View(user);
}
the reason is you are passing the same old model to the view see
[HttpPost]
public ActionResult ManageUser(User user){ <-- you get the posted model here
this.users_db.saveUser(model.user);<-- here it is saved
ViewBag.message="Success"; <--success msg
return View(user);<-- and here you are passing the recieved model as it is to the view
}

Is there a way to pass back the Request.Files in the view model on post (ASP.NET MVC 3)?

I've successfully removed references to Request.Form in my code, but I haven't seen any ASP.NET MVC 3 support for binding Request.Files to my view model. Would best practice be to just pass the Request.Files object (HttpFileCollectionBase) to whatever method processes my attachments? E.g.,
[HttpPost]
public ActionResult UpdateStatus(StatusViewModel vm)
{
bool updated = HandleUpdate(Request.Files, vm);
...
return View("Updated");
}
You can simply declare Action parameters of type HttpPostedFileBase, like so:
public ActionResult Method(HttpPostedFileBase fileUploaded)
You can bind to arrays of like-named instances, as well.
public ActionResult Method(HttpPostedFileBase[] filesUploaded)
and, HttpPostedFileBase can be a member of your view model.
public class MyModel{
public string Name{get; set;}
public HttpPostedFileBase FileUploaded{get; set;}
}

Catching and resolving AmbiguousMatchException

I would like to catch the AmbiguousMatchException whenever it is thrown and then write some code to resolve it. For example, I have an action ChangePassword which should be called only if the user is logged in. I have another method RenewPassword which must be called if the user is not logged in. I have given the same action name to both these methods.
[HttpPost]
[ActionName("ChangePassword")]
public ActionResult RenewPassword(ChangePasswordModel model)
{
...
}
[Authorize]
[HttpPost]
[ActionName("ChangePassword")]
public ActionResult ChangePassword(ChangePasswordModel model)
{
...
}
I want to use the same action name because I do not want the view to have to worry about which action to call. I know that I can write a custom FilterAttribute which will do the reverse of what AuthorizeAttribute does, apply it to the RenewPassword method, and thereby resolve the ambiguity. However, this seems like too much work for a very simple need.
Any better ideas? Is there a built in way to say that a particular action should be executed only for anonymous users and not for logged in users?
If you don't views having to worry about which action to call why not writing a reusable HTML helper:
public static class HtmlExtensions
{
public static MvcForm BeginChangePasswordForm(this HtmlHelper htmlHelper)
{
if (htmlHelper.ViewContext.HttpContext.User.Identity.IsAuthenticated)
{
return htmlHelper.BeginForm("ChangePassword", "SomeController");
}
return htmlHelper.BeginForm("RenewPassword", "SomeController");
}
}
and inside your view:
#using (Html.BeginChangePasswordForm())
{
...
}
and in the corresponding controller:
[HttpPost]
public ActionResult RenewPassword(ChangePasswordModel model)
{
...
}
[Authorize]
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
...
}

MVC ASP.NET MVC3 AllowHtml Attribute Not Working?

The question is very simple:
Say you have a model called Person
public class Person
{
public int PersonID {get; set;}
public string Name {get; set;}
[AllowHtml] // Allow html in Intro property
public string Intro {get; set;}
[ScaffoldColumn(false)]
public string ComplicatedValue {get; set;}
}
In controller's Create action
[HttpPost]
public ActionResult Create(Person o, FormCollection collection)
{
// whatever code here;
}
If you run it,
input plain text for Intro, no
problem happens.
input html content for Intro, no matter how you set
your configuration file, it will
tells "A potential dangerous ..."
I DO find the reason of this problem.
If you change the function to
public ActionResult Create(Person o) // Get rid of the *FormCollection collection*
{
// whatever code here;
}
This will eliminate the "potential dangerous" error.
But my problem is that for my application, I have to use the secondary parameter FormCollection collection in the Create Action method, because I need to use some other control values and server variable to assign a calculated value to the ComplicatedValue property.
If any expert of ASP.NET MVC3 have met the same problem as me, and found a solution, please kindly let me know.
This forum at this link discusses this issue at length and gives some workarounds.
http://forums.asp.net/p/1621677/4161637.aspx
Here is one solution from that thread that may or may not work for you:
public ActionResult Create(Person o) // Get rid of the *FormCollection collection*
{
FormCollection form = new FormCollection(Request.Unvalidated().Form);
// whatever code here;
}
or my own recommendation:
public ActionResult Create(Person o, int otherControlValue1, int otherControlValue2, ...)
{
o.ComplicatedValue = CalculateComplicatedValue(otherControlValue1, otherControlValue2, ...);
// whatever code here.
}
In my case, I am not using the FormCollection, but it was there so that I had a different footprint on my [HttpPost] method. I did this hack and put in a bogus parameter:
public virtual ActionResult Edit(int id)
{
return View(this.repository.GetById(id));
}
[HttpPost]
public virtual ActionResult Edit(int id, int? bogusID)
{
var d = repository.GetById(id);
if (TryUpdateModel(d))
{
repository.Save();
return RedirectToAction("Index");
}
return View();
}
Might I suggest using a custom model binder instead of pulling the complex data from a FormCollection. Scott Hanselman has a blog post on creating a custom model binder that would serve as a good template. In his post he puts together a DateTimeModelBinder that allows a DateTime property to be set either by a single input containing the date or a pair of inputs containing a date and a time.
have you tried
[Bind(Exclude="ComplicatedValue")]
:
[HttpPost]
public ActionResult Create([Bind(Exclude="ComplicatedValue")]Person o)
{
}
?
with that it allows you to exclude setting ComplicatedValue property on the form and still submit the object as a Person class.
hope that helps

Resources