I'm a bit of an MVC newbie, so you'll have to forgive what I imagine is an elementary question.
I created a custom viewmodel in order to have a multiselect list in my form:
public class CustomerFormViewModel
{
public Customer Customer { get; private set; }
public MultiSelectList CustomerType { get; private set; }
public CustomerFormViewModel(Customer customer)
{
Customer = customer
// this returns a MultiSelectList:
CustomerType = CustomerOptions.Get_CustomerTypes(null);
}
}
I found that my first attempt only captured the first value of the multiselect, and I guessed that this is because my create actions looked like this:
// GET: /Buyer/Create
public ActionResult Create() { ... }
// POST: /Buyer/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Customer customer) { ... }
So, I decided to change it to this:
// GET: /Buyer/Create
public ActionResult Create() { ... }
// POST: /Buyer/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(CustomerFormViewModel model) { ... }
So that I can get the full output from the MultiSelectList and parse it accordingly. Trouble is, this complains that there's no parameterless constructor for the viewmodel (and there isn't) - and I'm not sure the right way to go about fixing this. Nothing I've tried has worked and I really need some help!
In case it helps, my view looks like this:
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MySite.Controllers.CustomerFormViewModel>" %>
...
<% using (Html.BeginForm())
<%= Html.ListBox("CustomerType", Model.CustomerType)%>
...
Have you tried a custom ModelBinder. Not sure I'm understanding your code clearly but this could be your starting point:
public class CustomerFormViewModelBinder : DefaultModelBinder
{
protected virtual object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var model = new CustomerFormViewModel(customer)
}
}
I believe I got it:
public class CustomerModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var form = controllerContext.HttpContext.Request.Form;
Customer customer = base.BindModel(controllerContext, bindingContext) as Customer;
if (customer!= null)
{
customer.CustomerType= form["CustomerType"];
}
return customer;
}
}
Along with an entry in the global.asax file's Application_Start():
ModelBinders.Binders.Add(typeof(Customer), new CustomerModelBinder());
which puts the comma separated list of listbox selections in the field. e.g. "1,3,4".
Related
I am working with a WEB API application in ASP .NET Core 2.0 where I have a custom filter attribute that inherits from ActionFilterAttribute.
How can I access the Model object passed to a controller action in POST call, in ActionFilterAttribute.OnResultExecuted()
The mentioned method is passed a ResultExecutedContext object but I could not find an easy and reliable way to get Model object from it.
The filter I have, looks like the following:
public sealed class MyCustomFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
}
public override void OnResultExecuted(ResultExecutedContext context)
{
var model = ?????????????
}
public override void OnResultExecuting(ResultExecutingContext context)
{
base.OnResultExecuting(context);
}
}
and the controller looks like the following:
[Route("api/[controller]")]
public class MyController : Controller
{
[ServiceFilter(typeof(MyCustomFilter))]
[HttpPost]
public async Task<IActionResult> Post([FromBody] List<MyModel> data)
{
// my logic to process model here. In my filter, I want to access data, which is passed into this Post method
return Ok();
}
}
I set a private var in the controller and assign the posted model to it on the controllers action
private YourModel _yourModel;
public ActionResult MyAction(YourModel model)
{
_yourModel = model;
return View();
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
//Access _yourModel here
}
To access the model from the filterContext parameter passed in, you can use the below
var model = ((Controller)filterContext.Controller).ViewData.Model;
Try using TempData. Set tempdata to the model in your action.
TempData["Model"] = myModel;
Then access using the TempData on the context.
var x = ((Controller)filterContext.Controller).TempData["Model"];
Hope that's what you meant.
thanks
I might not be doing this right because I'm new to this framework, I hope you can help ^^
As you can see in the code below, in every action I'm finding the table I want to update and then I call the method in the Table.
public class TableController : Controller
{
private Lobby L;
public TableController()
{
L = Lobby.Instance;
}
public ActionResult Index(uint id)
{
Table T = L.Tables[id];
return View(T);
}
public ActionResult AddPlayer(byte pos, uint id)
{
Table T = L.Tables[id];
...
T.AddPlayer(p, pos);
...
}
...
}
But I've noticed that I'm doing the same thing in every method, so I though I could turn the table into an attribute so I don't need to find it for every action.
I would like to have something like this:
public class TableController : Controller
{
private Lobby L;
private Table T;
public TableController(uint tableId)
{
L = Lobby.Instance;
T = L.Tables[tableId];
}
public ActionResult Index()
{
return View(T);
}
public ActionResult AddPlayer(byte pos)
{
...
T.AddPlayer(p, pos);
...
}
Is there anything wrong in this approach?
If this is conceptually ok, how can I pass the table ID to my constructor?
This is not working :(
routes.MapRoute(
"Table",
"Table_{tableId}/{action}/",
new { controller = "Table", action = "Index"}
);
Typically, a Controller constructor is used to inject dependencies, not data. Moreover, at this stage, this.Request|Response|Session as well as other fundamental properties are still null.
Try this instead:
protected override void Initialize(RequestContext requestContext)
{
var tableId = Convert.ToUInt32(requestContext.RouteData.GetRequiredString("tableId"));
L = Lobby.Instance;
T = L.Tables[tableId];
base.Initialize(requestContext);
}
Pass the parameter to actions not to controller.
Controller will be created each time a request happens.
You can use static properties if you need them all time.
Static properties can be accessed from any controller or action.
Is it somehow possible to set alternate names for querystring parameters in ASP.NET MVC?
I have this simple controller Index action:
public ActionResult Index(color = "")
{
...
}
Calling http://mysite.com/mypage/?color=yellow works quite nicely, the color parameter automatically picks up its value "yellow" from the querystring.
But now I would like to have a localized variant of the same page, with “pretty” localized parameters, but still working with the same controller method. Example: http://mysite.com/mypage/?farve=gul. Here I would like “gul” to be passed in as the color parameter to the default Index() ation method.
How do I set mappings for alternate names for querystring parameters?
How do I set mappings for alternate names for querystring parameters?
You could write a custom model binder.
So as in every ASP.NET MVC application you start by writing a view model:
public class MyViewModel
{
public string Color { get; set; }
}
and then a model binder for this model:
public class MyViewModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var query = controllerContext.HttpContext.Request.QueryString;
var value = query["color"] ?? query["gul"] ?? query["couleur"];
return new MyViewModel
{
Color = value,
};
}
}
which will be registered at your Application_Start:
ModelBinders.Binders.Add(typeof(MyViewModel), new MyViewModelBinder());
and now your controller action may take the view model as parameter:
public ActionResult Index(MyViewModel model)
{
...
}
Of course you could make the model binder more flexible by using some custom attribute on the property:
public class MyViewModel
{
[PossibleQueries("color", "gul", "couleur")]
public string Color { get; set; }
}
and in the model binder read those values and try reading them from the query string until you find one that is not null.
How about a second controller with a localized/pretty name where the actions and parameters have localized names and call the actions from the default/english controller? With this method you have all parts of the url localized.
Controller mypage
{
ActionResult Index(string color)
{
// normal code
}
}
Controller meineseite
{
ActionResult Index(string farbe)
{
return mypage.Index(farbe);
}
}
I have a model I want to use for communication with an external web service. It's supposed to call a specific post action on my website.
public class ConfirmationModel{
...
public string TransactionNumber {get; set;}
}
public ActionResult Confirmation(ConfirmationModel){
...
}
The problem is the parameters names they pass are not very human-readable. And I want to map them to my more readable model.
't_numb' ====> 'TransactionNumber'
Can this be done automatically? With an attribute maybe? What's the best approach here?
Create a model binder:
using System.Web.Mvc;
using ModelBinder.Controllers;
public class ConfirmationModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = new ConfirmationModel();
var transactionNumberParam = bindingContext.ValueProvider.GetValue("t_numb");
if (transactionNumberParam != null)
model.TransactionNumber = transactionNumberParam.AttemptedValue;
return model;
}
}
Initialise it in Global.asax.cs:
protected void Application_Start()
{
ModelBinders.Binders.Add(typeof(ConfirmationModel), new ConfirmationModelBinder());
}
Then in your action method
[HttpPost]
public ActionResult Confirmation(ConfirmationModel viewModel)
You should see the value of t_numb appear in TransactionNumber property of the viewmodel.
Agree that model binder is better: here's an alternate idea though
public ActionResult Create(FormCollection values)
{
Recipe recipe = new Recipe();
recipe.Name = values["Name"];
// ...
return View();
}
and a good read about both: http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx
I got a controller in my mvc application like below.
public class BaseController: Controller
{
protected void LogInfo()
{
logger.InfoFormat("[SessionID: {0}, RemoteIP: {1}]", Session.SessionID, Request.UserHostAddress); }
}
public class FirstController : BaseController
{
public ActionResult Index(string name)
{
LogInfo();
getQueryString();
if(IsValidRec())
{
if(Errors()))
{
return View("Error");
}
var viewname = getViewName(name);
return view(viewname);
}
else
return view("NotFound");
}
}
I need to create another controller(SecondController ) with same ActionResult method that FirstController has but without any implementation. Because I dont wont to repeat same code in 2 ActionResult methods.
what is the best way to do that. I tried in following way but I m getting error when I initialize my protected method 'LogInfo()'
public class SecondController : BaseController
{
public ActionResult Index(string name)
{
var firstcontroller = new FirstController();
return firstcontroller.Index(name);
}
}
Put the part you want to re-use in the base controller
e.g.
public class BaseController: Controller
{
protected void LogInfo()
{ ... }
virtual public ActionResult Index(string name)
{
LogInfo();
getQueryString();
.....
var viewname = getViewName(name);
return view(viewname);
}
}
public class FirstController : BaseController
{
override public ActionResult Index(string name)
{
var result = base.Index(name);
.... do more stuff ...
return result;
}
}
public class SecondController : BaseController
{
// Don't need to override index if you
// want to do the same as in the base controller
}
You can use inheritance like this (one way):
public abstract class MyControllerBase : Controller
{
// whatever parameters
protected SharedModel GetSharedModel()
{
// do logic
// return model
}
}
public class OneController : MyControllerBase
{
protected ActionResult Index()
{
var model = this.GetSharedModel()
return this.View(model);
}
}
public class TwoController : MyControllerBase
{
protected ActionResult Index()
{
var model = this.GetSharedModel()
return this.View(model);
}
}
It is better to put common functionality somewhere else in application and use that in both controller. Like we used to write helper classes or use shared services. It is not good to create the instance of controller and calling the Action method from that...from the Architectural point of view. If u still have doubt...please explain more about your common functionality...then I will be able to help more.
There are two solutions to this type of issue: inheritance and composition.
In general, inheritance has less code, but is less flexible.
Here's a more through discussion.