My action controller code looks amateurish - asp.net-mvc

First post time,
I've been playing around with MVC abit... I have a view which has multiple input fields, some of these fields can be blank on post.
The action method inside the controller for the post looks something like this
public ActionResult Filter(int? id, string firstName, string lastName, bool? isMember)
I've been using the DynamicQuery extension which has been kicking around in order to perform dynamic Linq querys on my database and I've encapsulated this in a Search object which is passed to the data access layer for execusion.
However, I also have a customized ViewData object which is passed back to the view for displaying the input values and the results of the query.
It all looks a little nasty in code as I'm having to set both the Search object properties AND the ViewDatas.
public ActionResult Filter(int? id, string firstName, string lastName, bool? isMember) {
var search = new Search {
Id = id,
FirstName = firstName,
LastName = lastName,
Member = isMember
};
var memberViewData = new MemberViewData {
Id = id,
FirstName = firstName,
LastName = lastName,
Member = isMember
};
memberViewData.Results = _dataRepository.GetMember(search);
return View("Search", memberViewData);
}
Am I over thinking this and really should just pass the values to the data access layer and populate the ViewData in the controller, or is there a much more elegant pattern or practise I could use?
Sorry if this seems dump, not allot of people to bounce ideas off and time to dig into the framework.

Use modelbinder to bind data

According to your snippet MemberViewData class has the Results property in addition to properties of Search class. So first step would be to make MemberViewData derive from Search and define a constructor that accepts Search instance as parameter and assigns it's basic properties from it. Next I would change the action method like so:
public ActionResult Filter(Search search)
{
return View("Search", new MemberViewData(search)
{
Results = _dataRepository.GetMember(search)
});
}

Like Tadeusz mentioned, a ModelBinder can help build the MemberViewData for you, which would leave only the results to be fetched.
You could also decide on creating a presentation service that understands how to build this view data object and simply delegate to it. I'd prefer the model binder approach here though.

Related

How does MVC 5 generate url for GET redirect action result?

So in my mvc 5 app, I got this default 'Index' action, which simply redirects to the search action, with the default model values:
[Route]
public ActionResult Index()
{
var model = new T();
return RedirectToAction("Search", model);
}
What i got puzzled about is how i end up with the url like '.../search?xxx=xxx...'? Is there anything i can do to customize or at least inject/replace the url generation, especially the query string part? e.g. i might want to display 1/0 for bool properties in the search model, and customize query string key names etc?
and why would someone vote down for my question? psst...
The query string generated depend on the model property name and type + value you passed in.
For example, say if your pass in model is like
public class MyModel
{
bool IsSort{get;set;}
}
If your model is like this and IsSort value is true, then you will get url like /search?IsSort=true
Say you want to change your query string to 1/0 instead of true or false, then create viewModel which has property string then assing it properly like:
public class MyModel
{
string IsSort{get;set;}
}
var model = new MyModel();
model.IsSort = true? "1":"0";
same apply to querystring key (which correspond to property name)

passing value in partial view viewdatadictionary

#Html.Partial("~/Areas/WO/Views/PartialContent/_FirstPage.cshtml", new ViewDataDictionary { { "WOID", WOID } })
In my Page i am accessing Partial view in the above way.
I need to pass WOID(view data dictionary) value from query string, For that i am using following Code
#{
var desc = Html.ViewContext.HttpContext.Request.QueryString.Get("ID");
Uri referrer = HttpContext.Current.Request.UrlReferrer;
string[] query = referrer.Query.Split('=');
int WOID = Convert.ToInt32(query[1]);
}
But the issue is this code is working in all browsers except I.E. i Need to Solve this problem.
Please help me
Instead of this you can have this value as part of you model and use that.That is the standard and recommeded way .
In your action method you can have these as parameter.Your query string value will get bind to this parameter
public ActionResult ActionMethod(int ID)
{
Model.WOID = WOID;
// Other logic
return View(Model)
}
Next step you can add this as a property to your view model or add it to ViewData dictionary and then access it in your partial view.

missunderstanding mvc default binding

I have multiselect jquery plagin (Choosen) and when I use it in 'Multiple Select' mode I expect in controller next values:
posted string = 'value1,value2...'
really have
posted string = 'value2'
only if I reffer directly to FormCollection I'll get expected values as below:
[HttpPost]
public ActionResult TagSearech(/*string tagSelect*/FormCollection c)
{
// only one value here
// string[] names = tagSelect.Split(',');
// as expected: value1,....
string expectedValue = c['tagSelect'];
return View();
}
I cant understand what might cause this behavior.
EDIT
Here is View:
#using (Html.BeginForm("TagSearech", "Tag"))
{
#Html.DropDownList("tagSelect", Model, new { #class = "chzn-select", data_placeholder = "tag names", multiple = "" })
<input type="submit"/>
}
MVC will attempt to bind the input data on the URL into the model. I haven't seen how Chosen.js posts the data back to the server, but essentially its coming in in the wrong format, so MVC binds the first element it sees to the string Model.
The FormsCollection retrieves all of the data that was posted in the URL, which is why all of your selected values can be seen there.
Did you try changing the incoming model from string to string[], and see if all of the items are bound to the array?

passing variable to view ASP MVC

I have 2 views, one to return all the locations belonging to a project, the other returns a json file containing the locations that used to show them on a google map.
Listing the locations works like this as the id is sent with the actionlink, but how do I send the project ID to the Map view?
public ActionResult GoogleMaps(int id)
{
return View(Project.Find(id).DeviceLocations);
}
//[AutoRefresh(DurationInSeconds = 30)]
public ActionResult Map(int id)
{
var map = Project.Find(id).DeviceLocations;
Locations l = new Locations();
l.locations = map;
return Json(l, JsonRequestBehavior.AllowGet);
}
Some people construct a model (class) specifically to handle all of the values being passed from the controller. So, your model would have a property called DeviceID, and would thus be strongly typed.
Alternately, you can use ViewData:
You can put this in your controller:
ViewData["DeviceID"] = id;
Then, in your view, you will need to cast it before you use it, like this:
(int)ViewData["DeviceID"]
Include a get, set property "project ID" in your Locations class.

Model binding in ASP.NET MVC when editing an entity

i am kind of stuck with this code:
[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Edit(FormCollection collection, [Bind(Exclude="Id,Latitude,Longitude")]Store bindStore, string Latitude, string Longitude)
{
Store st = storeModel.GetStore(Session["user_id"].ToString());
bool modelUpdate = TryUpdateModel(st, new string[] { "Storename", "Address1", "Address2", "Zipcode", "City", "Phone1", "Phone2", "Store_email", "Store_homepage", "Pm_ec", "Pm_visa", "Pm_master", "Appointment_requirement", "Short_user_store_description" });
if (ModelState.IsValid)
{
storeModel.Save();
return RedirectToAction("ImageUpload");
}
else
return Edit();
}
My problem is, that i don't know whats the right way to do the ModelBinding, when i need these conditions to match:
The "Store" object has several fields ( not only Id ) that should NOT be updated during this update ( had problems that the "TryUpdateModel" NULL-ifies all these fields, so i added the "includeProperties" value with the cleartext of all the params.. a solution i don't like anyway)
Getting the store id from session as i don't want it to be in the HTML
Making InputValidation through the DataAnnotationModel ( see below )
[MetadataType(typeof(StoreMetaData))]
public partial class Store
{
}
public class StoreMetaData
{
[Required]
[StringLength(5)]
public object Storename { get; set; }
}
Question: Is this the right approach or is there a standard/nicer solution for this kind of problem?
You don't need formcollection in parameters for start.
And no, updatemodel will try to update all fields of model if no white or black list defined.
So either that or create your own function to update your model objects.
Maybe somethig generic. that will reflection enumerate properties of supplied update object and apply those that are not null to object being updated.
My guess, from looking at the code you posted, is that you're trying to make it so that the Edit view will only allow certain fields to be edited, but 'pass through' the ones you don't want changed. In that case, you can modify your Edit view to remove those fields from being edited, but still send them over in the form using:
<%= Html.Hidden("field") %>
Let me know if this is what you intended.
Your way is fine IMHO.
There are other options but none of them provide a compelling benefit over your technique.
The only thing I would do differently is to model bind to custom ViewModel with the exact fields you need and then do the rightly-lefty code to assign just those values:
public ActionResult Edit( SlimStoreViewmodel editStoreModel, string Latitude, string Longitude)
{
Store st = storeModel.GetStore(Session["user_id"].ToString());
if (ModelState.IsValid)
{
st.Thing = editStoreModel.Thing;
st.Thing2 = editStoreModel.Thing2;
st.Widget = editStoreMOdel.Widget;
storeModel.Save();
return RedirectToAction("ImageUpload");
}
else
return Edit();
}
This gets rid of long magic string dense things like this:
TryUpdateModel(st, new string[] { "Storename", "Address1", "Address2", "Zipcode", "City", "Phone1", "Phone2", "Store_email", "Store_homepage", "Pm_ec", "Pm_visa", "Pm_master", "Appointment_requirement", "Short_user_store_description" });

Resources