Renaming ASP.NET MVC 4 Properties for Publishing
Follwing agreements are given. Each Action
has a single type parameter BaseRequest
with data dependent on Action
. ViewModel
always has a type BaseResponse
.
What I'm trying to do is that if a View
contains a form, the POST
-Action requires some sort of BaseRequest
. How can I achieve correct model binding as well ViewModel
- BaseResponse
?
I already tried to add a property XYZRequest
to XYZResponse
so that I can communicate as
@Html.ChecBoxFor(m => m.RequestObject.SomeBooleanProperty)
but this will generate a name
RequestObject.SomeBooleanProperty that will not bind correctly to the POST action it takes XYZRequest
.
Is there something completely wrong with these conventions, or am I missing something?
Update # 1
I also tried to create a new temporary
type object XYZRequest
and bind to it like
@Html.CheckBoxFor(m = tmpReq.SomeBooleanProperty)
which will display a name
of tmp.SomeBooleanProperty
which also cannot be linked.
Update # 2 - additional information
The following structure is established.
-
BaseRequest
-abstract
-
GetOverviewRequest
:BaseRequest
-
GetOverviewRequest
has properties of a typestring
,int
or any other complex type, and evenLists
orDictionaries
If GetOverviewResponse
that inherits from BaseResponse
returns to View
and provides a property named type, communication fails TheProperty
GetOverviewRequest
@Html.TextBoxFor(m => m.TheProperty.SomeBooleanValue)
will try to bind to TheProperty
-property on an object GetOverviewRequest
that just doesn't exist.
This can work if it GetOverviewRequest
has a property called TheProperty
for binding. But if it is named differently, the linking will also fail.
I just want something like
<input name="SomeBooleanValue">
<input name="SomeComplexType.SomeStringValue">
instead
<input name="TheProperty.SomeBooleanValue">
<input name="TheProperty.SomeComplexType.SomeStringValue">
Update # 3 - Added Sample Project
Sample project via dropbox.com
Update # 4 - Explanation why solution from @StephenMuecke doesn't work
As mentioned in the comments, the solution in another question is to know the name of the property in the object GetOverviewResponse
. The property has a name TheProperty
, so I must add [Bind(Prefix = "TheProperty)]
to enable correct binding. I really don't like magic strings. And "TheProperty"
- the magic line. If you change the name TheProperty
to RenamedProperty
, full linking will fail.
So. Now I'm looking for a way to set the prefix in some way dynamically.
[Bind(Prefix = GetOverviewResponse.NameOf(m => m.TheProperty))]
it would be really amazing. Maybe some custom attribute? Since the BindAttribute is sealed, there is no way to create an inheritance from this.
Any ideas?
source to share
As pointed out in the comments, I created a new framework to achieve what I need.
I created a new class BaseRequestWrapperResponse<TRequest>
to encapsulate an object Request
inResponse
public abstract class BaseRequestWrapperResponse<TRequest> : BaseResponse where TRequest : IRequestFromResponse
This class has one property:
public TRequest Request { get; set; }
Next, I created an interface:
public interface IRequestFromResponse
from which my Requset
-object will inherit -> will be used for binding.
In my custom modelbinder override, protected override object CreateModel
I check if (modelType.GetInterfaces().Contains(typeof(IRequestFromResponse)))
to see if my Request
special handling is needed . If so, I dynamically create BindAttribute
and set it before linking normal
does the rest:
var bindAttribute = new BindAttribute {Prefix = GetPropertyName<BaseRequestWrapperResponse<IRequestFromResponse>, IRequestFromResponse>(r => r.Request)};
TypeDescriptor.AddAttributes(modelType, bindAttribute);
where the GetPropertyName property is defined:
private static string GetPropertyName<TSource, TProperty>(Expression<Func<TSource, TProperty>> propertyLambda)
{
var member = (MemberExpression)propertyLambda.Body;
return member.Member.Name;
}
See also: Get the name of a property in an abstract generic class
source to share
Binding in MVC works in the Name / Value dictionary. Therefore, if you have:
public class BaseRequest
{
public string prop1 {get; set;}
public string prop2 {get; set;}
public string prop3 {get; set;}
}
cshtml:
@Html.TextBoxFor(x => x.prop1)
Then the object the controller takes is irrelevant:
public ActionResult MyAction(NotBaseRequest request)
{
//do something
}
new object taken in:
public class NotBaseRequest
{
public string prop1 {get; set;}
}
MVC will link this with no problem.
So, if you want the child to be bound, you must have a base object in the object that the controller takes:
public class BaseRequest
{
public NotBaseRequest NotBaseRequest {get; set;}
}
cshtml
@Html.TextBoxFor(x => x.NotBaseRequest.prop1)
//<input type="text" name="NotBaseReqest.prop1" value />
MVC will use the name attribute to send the value to the controller.
controller
public ActionResult MyAction(OtherRequest request)
{
//do something
}
The object you are taking can be called anything as long as it has a NotBaseRequest in it.
new object:
public class OtherRequest
{
public NotBaseRequest NotBaseRequest {get; set;}
}
The name attribute of the html object will create a child object and assign its values.
source to share