Asp.Net MVC 2 - Bind a model's property to a different named value
You can use the BindAttribute to accomplish this.
public ActionResult Submit([Bind(Prefix = "L")] string[] longPropertyName) {
}
Update
Since the 'longPropertyName' parameter is part of the model object, and not an independent parameter of the controller action, you have a couple of other choices.
You could keep the model and the property as independent parameters to your action and then manually merge the data together in the action method.
public ActionResult Submit(MyModel myModel, [Bind(Prefix = "L")] string[] longPropertyName) {
if(myModel != null) {
myModel.LongPropertyName = longPropertyName;
}
}
Another option would be implementing a custom Model Binder that performs the parameter value assignment (as above) manually, but that is most likely overkill. Here's an example of one, if you're interested: Flags Enumeration Model Binder.
Model Binding to Multiple Types with Same Property Names
Use:
public JsonResult Submit([Bind(Prefix = "audience")]Audience aud,[Bind(Prefix = "locale")]Locale loc)
{
// do stuff
}
Every Audience field should have "audience" prefix in name in html field: audience.id,audience.name,...
<input id="audience_name" name="audience.Name" type="text" value="" />
Bind to a POCO model using different property names
You could add data annotations over your POCO Model.
Here is better explanation: https://stackoverflow.com/a/15916121/9233618
public class TeamScore
{
[JsonProperty("first_name_text ")]
public string FirstName { get; set; }
[JsonProperty("last_name_text ")]
public string LastName { get; set; }
}
MVC Model that includes property of a different Model
I was until now unaware of this thing called a "Navigation Property". So Site
, being a property of a Device
which is it's own Model, links the two tables together.
So what I was missing, as Steve Greene had clued me in on, was Including Site
along with Device
in the models for the views in which I wanted to use the Site's Name (in the list for Index, and using a Predicate for Details and Delete)...called "Eager Loading".
I also moved the code for generating the list of items for the DropDownFor to the Controller, as suggested, using a ViewData. Now as stated in the question, because that is passing back only the Site ID I have to manually set the Device's Site based on the ID of the item they selected.
So here is what my Devices Controller looks like now:
Async Function Index() As Task(Of ActionResult)
'Need to have this include Site so that it will pull in that data in order to reference site name in the View
'(doing this is called Eager Loading)
Dim devices = db.Devices.Include("Site").ToListAsync()
Return View(Await devices)
End Function
' GET: Devices/Details/5
Async Function Details(ByVal id As Integer?) As Task(Of ActionResult)
If IsNothing(id) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Dim device As Device = Await db.Devices.FindAsync(id)
If IsNothing(device) Then
Return HttpNotFound()
Else
'Need to have this include Site using Predicate so that it will pull in that data in order to reference site name in the View
device = db.Devices.Include("Site").Where(Function(x) x.DeviceID = id).FirstOrDefault()
End If
Return View(device)
End Function
' GET: Devices/Create
Function Create()
'Create the list of items that the DropDownFor in the View will reference
Dim selectSite As New List(Of SelectListItem)
Dim db = New TestWebApplication2.Models.TestWebApplication2Context
Dim listOfSites As List(Of Site)
listOfSites = (From site In db.Sites Select site).ToList()
For Each item In listOfSites
selectSite.Add(New SelectListItem() With {.Value = item.SiteID, .Text = item.Name})
Next
ViewData("selectSite") = selectSite
Return View()
End Function
' POST: Devices/Create
'To protect from overposting attacks, please enable the specific properties you want to bind to, for
'more details see http://go.microsoft.com/fwlink/?LinkId=317598.
<HttpPost()>
<ValidateAntiForgeryToken()>
Async Function Create(<Bind(Include:="DeviceID,Make,Model,Serial")> ByVal device As Device) As Task(Of ActionResult)
If ModelState.IsValid Then
'Because Site is selected in a DropDownFor which only passes the ID integer, need to set the device's site property accordingly
'Note that with Site being a Navigation property, it looks like it doesn't get saved the way you would think...but it all works
Dim thisSite As Integer = CInt(Request.Form.Get("Site"))
device.Site = (From site In db.Sites Where site.SiteID = thisSite Select site).FirstOrDefault()
db.Devices.Add(device)
Await db.SaveChangesAsync()
Return RedirectToAction("Index")
End If
Return View(device)
End Function
' GET: Devices/Edit/5
Async Function Edit(ByVal id As Integer?) As Task(Of ActionResult)
If IsNothing(id) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Dim device As Device = Await db.Devices.FindAsync(id)
If IsNothing(device) Then
Return HttpNotFound()
Else
'Create the list of items that the DropDownFor in the View will reference
Dim selectSite As New List(Of SelectListItem)
Dim db = New TestWebApplication2.Models.TestWebApplication2Context
Dim listOfSites As List(Of Site)
listOfSites = (From site In db.Sites Select site).ToList()
For Each item In listOfSites
selectSite.Add(New SelectListItem() With {.Value = item.SiteID, .Text = item.Name})
Next
ViewData("selectSite") = selectSite
End If
Return View(device)
End Function
' POST: Devices/Edit/5
'To protect from overposting attacks, please enable the specific properties you want to bind to, for
'more details see http://go.microsoft.com/fwlink/?LinkId=317598.
<HttpPost()>
<ValidateAntiForgeryToken()>
Async Function Edit(<Bind(Include:="DeviceID,Make,Model,Serial")> ByVal device As Device) As Task(Of ActionResult)
If ModelState.IsValid Then
'Because Site is selected in a DropDownFor which only passes the ID integer, need to set the device's site property accordingly
'Note that with Site being a Navigation property, it looks like it doesn't get saved the way you would think...but it all works
Dim thisSite As Integer = CInt(Request.Form.Get("Site"))
device.Site = (From site In db.Sites Where site.SiteID = thisSite Select site).FirstOrDefault()
db.Entry(device).State = EntityState.Modified
Await db.SaveChangesAsync()
Return RedirectToAction("Index")
End If
Return View(device)
End Function
' GET: Devices/Delete/5
Async Function Delete(ByVal id As Integer?) As Task(Of ActionResult)
If IsNothing(id) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Dim device As Device = Await db.Devices.FindAsync(id)
If IsNothing(device) Then
Return HttpNotFound()
Else
'Need to have this include Site using Predicate so that it will pull in that data in order to reference site name in the View
device = db.Devices.Include("Site").Where(Function(x) x.DeviceID = id).FirstOrDefault()
End If
Return View(device)
End Function
' POST: Devices/Delete/5
<HttpPost()>
<ActionName("Delete")>
<ValidateAntiForgeryToken()>
Async Function DeleteConfirmed(ByVal id As Integer) As Task(Of ActionResult)
Dim device As Device = Await db.Devices.FindAsync(id)
db.Devices.Remove(device)
Await db.SaveChangesAsync()
Return RedirectToAction("Index")
End Function
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If (disposing) Then
db.Dispose()
End If
MyBase.Dispose(disposing)
End Sub
How to bind URL parameters to model properties with different names
The model binder matches the parameters it gets from the view to the model you have in the action by the Names, so if they won't match the binding will not work.
options you got:
- Match the inputs names to the model properties names... but You said you can't do this, (for some unknown reason).
- Write custom Model Binder. *
- Use The Bind attribute with prefix - though it'll still force you to have input names close to the model properties names.
so basically, You can't do exactly what you want.
Update:
You wrote in a comment that the properties CAN match the parameters names, so instead of write custom attributes that maybe will succeeded to do the binding, just write a ViewModel (The VM fromMVC...) to adjust the url parameters names.
Writing custom model binder is not recommended by the MVC team:
In general, we recommend folks don’t write custom model binders because they’re difficult to get right and they’re rarely needed
from here
MVC 3 Bind Model property to 2 fields (e.g. Other Title)
a better solution is not to bind to two fields, instead, copy selected item from a drop-down into bound textbox with some clever javascript:
@Html.DropDownList("ddlTitle", new SelectList(Model.TitleList), "Please select")
@Html.TextBoxFor(m => m.Title, new { maxLength = 10 })
Javascript:
ToggleTitleFields = function () {
var title, txtTitle;
title = $('select#ddlTitle').val();
txtTitle = $('input#Title');
if (title === "Other") {
txtTitle.val("");
txtTitle.show();
return txtTitle.focus();
} else {
txtTitle.hide();
txtTitle.val(title);
return $('span[data-valmsg-for="Title"]').empty();
}
};
$(document).on("change", "select#ddlTitle", function(e) {
return ToggleTitleFields();
});
hope this helps somebody
Related Topics
Ide's for C# Development on Linux
How to Write an Async Method with Out Parameter
How to Get Variable Name Using Reflection
Adding Items to a List<> of Objects Results in Duplicate Objects When Using a New in a Loop
Find If Current Time Falls in a Time Range
Get the Titles of All Open Windows
Why Are Extension Methods Only Allowed in Non-Nested, Non-Generic Static Class
Replace Multiple Characters in a C# String
Recursively Get Properties & Child Properties of a Class
How to Format a Datetime in a Different Format
How to Serialize a C# Anonymous Type to a JSON String