ASP.NET MVC 4 property-renaming for posting

限于喜欢 提交于 2019-12-12 03:34:15

问题


Follwing convention(s) are given. Each Action has a single parameter of type BaseRequest with data depending on the Action. The ViewModel is always of 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 of the ViewModel is BaseResponse?

I already tried to add a property of XYZRequest in XYZResponse so I could bind like

@Html.ChecBoxFor(m => m.RequestObject.SomeBooleanProperty)

but this will generate name RequestObject.SomeBooleanProperty which will not bind correctly to the POST-Action which accepts the XYZRequest.

Is there something completely wrong with this kind of conventions or am I missing something?

Update #1

I also tried creating a new temporary object of type XYZRequest and bind to it like

@Html.CheckBoxFor(m = tmpReq.SomeBooleanProperty)

which will render a name of tmp.SomeBooleanProperty which also could not be bound.

Update #2 - additional information

Following structure is set.

  • BaseRequest is abstract
  • GetOverviewRequest : BaseRequest
  • GetOverviewRequest has properties of type string, int or any other complex type and even Lists or Dictionaries

If the GetOverviewResponse, which inherits from BaseResponse, is given back to the View and provides a property named TheProperty of type GetOverviewRequest binding fails as of

@Html.TextBoxFor(m => m.TheProperty.SomeBooleanValue)

will try to bind to TheProperty-property in the GetOverviewRequest object, which just not exists.

This may would work if GetOverviewRequest has a property called TheProperty to bind. But if it is named differently, binding will also fail.

I just want something like

<input name="SomeBooleanValue">
<input name="SomeComplexType.SomeStringValue">

instead of

<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 is not working

As mentioned in comments, the solution in other question needs to know the name of the property in the GetOverviewResponse-object. The property is named TheProperty, therefore I have to add [Bind(Prefix = "TheProperty)] to enable correct binding. I really don't like magic strings. And "TheProperty" is a magic string. If one changes the name of the TheProperty to RenamedProperty, the whole binding will fail.

So. I'm now looking for a way to set the prefix some-kind dynamically.

[Bind(Prefix = GetOverviewResponse.NameOf(m => m.TheProperty))]

would be really awesome. Maybe some kind of a custom attribute? As of BindAttribute is sealed, there is no chance to create one inherited from this.

Any ideas?


回答1:


The binding in MVC works on a Name / Value dictionary. So 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 that the controller accepts doesn't matter:

public ActionResult MyAction(NotBaseRequest request)
{   
    //do something
}

new object taken in:

public class NotBaseRequest
{
    public string prop1 {get; set;}
}

MVC would bind this with no problem.

So if you want a child object to be bound then you have to have the base object in the object that the controller takes in:

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 take in can be called anything as long as it has NotBaseRequest in it.

new object:

public class OtherRequest
{
    public NotBaseRequest NotBaseRequest {get; set;}
}

The name attribute of the html object will create the child object and assign its values.




回答2:


As stated by comments I've created some new structure to achieve what I needed.

I created a new class BaseRequestWrapperResponse<TRequest> to encapsulate the Request-object in a Response

    public abstract class BaseRequestWrapperResponse<TRequest> : BaseResponse where TRequest : IRequestFromResponse

This class has currently one property:

    public TRequest Request { get; set; }

Next I've created an interface:

    public interface IRequestFromResponse

from which my Requset-object will inherit -> will be used for binding.

In my custom modelbinder override of protected override object CreateModel I check if (modelType.GetInterfaces().Contains(typeof(IRequestFromResponse))) to see if my Request needs special handling. If so I create the BindAttribute dynamically and set it before the normal binder will do the rest:

                var bindAttribute = new BindAttribute {Prefix = GetPropertyName<BaseRequestWrapperResponse<IRequestFromResponse>, IRequestFromResponse>(r => r.Request)};
            TypeDescriptor.AddAttributes(modelType, bindAttribute);

where GetPropertyName 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 name of property in abstract generic class



来源:https://stackoverflow.com/questions/31616609/asp-net-mvc-4-property-renaming-for-posting

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!