【问题标题】:ASP.NET MVC 4 property-renaming for posting用于发布的 ASP.NET MVC 4 属性重命名
【发布时间】:2015-07-24 17:39:39
【问题描述】:

给出了以下约定。每个Action 都有一个BaseRequest 类型的参数,其数据取决于ActionViewModel 始终是 BaseResponse 类型。

我想做的是,如果View 包含一个表单,那么POST-Action 需要某种BaseRequest。 在ViewModelBaseResponse 时,如何实现正确的模型绑定?

我已经尝试在XYZResponse 中添加XYZRequest 的属性,所以我可以像这样绑定

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

但这会生成name RequestObject.SomeBooleanProperty,它不会正确绑定到接受XYZRequest的POST-Action。

这种约定是完全错误的还是我遗漏了什么?

更新 #1

我还尝试创建一个 XYZRequest 类型的新 temporary 对象并像这样绑定到它

@Html.CheckBoxFor(m = tmpReq.SomeBooleanProperty)

这将呈现 tmp.SomeBooleanPropertyname 也无法绑定。

更新 #2 - 附加信息

设置了以下结构。

  • BaseRequestabstract
  • GetOverviewRequestBaseRequest
  • GetOverviewRequest 具有 stringint 或任何其他复杂类型甚至 ListsDictionaries 类型的属性

如果继承自BaseResponseGetOverviewResponse 被返回给View 并提供一个名为TheProperty 的属性,类型为GetOverviewRequest 绑定失败 p>

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

将尝试绑定到 GetOverviewRequest 对象中的 TheProperty-property,该对象不存在。

如果GetOverviewRequest 有一个名为TheProperty 的属性要绑定,这可能会起作用。但是如果命名不同,绑定也会失败。

我只想要类似的东西

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

而不是

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

更新 #3 - 添加示例项目

Sample project via dropbox.com

更新 #4 - 解释,为什么 @StephenMuecke 的解决方案不起作用

如cmets中所说,其他问题的解决方案需要知道GetOverviewResponse-object中的属性名称。该属性名为TheProperty,因此我必须添加[Bind(Prefix = "TheProperty)] 才能启用正确绑定。我真的不喜欢魔线。而"TheProperty" 是一个神奇的字符串。如果把TheProperty的名字改成RenamedProperty,整个绑定就会失败。

所以。我现在正在寻找一种动态设置前缀的方法。

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

真的很棒。也许某种自定义属性?由于 BindAttribute 是密封的,因此没有机会创建一个继承自此的属性。

有什么想法吗?

【问题讨论】:

  • 你能把你正在使用的模型贴在页面上吗?
  • @Robert 添加了该项目。这有帮助吗?
  • @StephenMuecke 这可以解决问题。但我正在寻找更有活力的东西。因为如果更改名称TheProperty 但没有更改绑定前缀,这也会失败。任何想法如何动态地做到这一点?像[Bind(Prefix=new ResponseObject().GetPropertyName(TheProperty))] 或类似的东西?
  • 不,这里没有内置的东西可以做到这一点 - 你必须编写自己的自定义 ModelBinder

标签: c# asp.net-mvc-4 razor extension-methods model-binding


【解决方案1】:

MVC 中的绑定适用于名称/值字典。所以如果你有:

public class BaseRequest
{
    public string prop1 {get; set;}
    public string prop2 {get; set;}
    public string prop3 {get; set;}
}

cshtml:

@Html.TextBoxFor(x => x.prop1)

那么控制器接受的对象就无所谓了:

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

新的对象:

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

MVC 可以毫无问题地绑定它。

所以如果你想绑定一个子对象,那么你必须在控制器接收的对象中拥有基础对象:

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

cshtml

@Html.TextBoxFor(x => x.NotBaseRequest.prop1)
//<input type="text" name="NotBaseReqest.prop1" value /> 

MVC 将使用 name 属性将值发送到控制器。

控制器

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

你接收的对象可以叫任何东西,只要它里面有NotBaseRequest。

新对象:

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

html 对象的 name 属性将创建子对象并为其赋值。

【讨论】:

  • 查看我编辑的问题。也许我只是不明白,或者不清楚我想要实现什么
  • @KingKerosin 您是否试图将结果扁平化为具有原始类型的对象?
  • 没有。 Request 可以有原始类型和复杂类型。复杂类型的事件列表和字典。因此绑定response =&gt; response.ReqObject.PrimitiveType 将正确绑定到表单的POST-Action 预期的请求对象,因此输入名称将是ReqObjectsName.PrimitiveType 而不是ResponseObjectsName.ReqObjectsName.PrimitiveType。编辑问题
【解决方案2】:

正如 cmets 所说,我创建了一些新结构来实现我所需要的。

我创建了一个新类 BaseRequestWrapperResponse&lt;TRequest&gt;Request 对象封装在 Response

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

这个类目前有一个属性:

    public TRequest Request { get; set; }

接下来我创建了一个界面:

    public interface IRequestFromResponse

我的Requset-object 将从那里继承 -> 将用于绑定。

在我的自定义模型绑定器覆盖protected override object CreateModel 我检查if (modelType.GetInterfaces().Contains(typeof(IRequestFromResponse))) 看看我的Request 是否需要特殊处理。如果是这样,我会动态创建BindAttribute 并在normal 活页夹完成其余工作之前对其进行设置:

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

GetPropertyName 的定义位置:

        private static string GetPropertyName<TSource, TProperty>(Expression<Func<TSource, TProperty>> propertyLambda)
    {
        var member = (MemberExpression)propertyLambda.Body;
        return member.Member.Name;
    }

另请参阅:Get name of property in abstract generic class

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-12
    • 1970-01-01
    • 2015-01-07
    • 1970-01-01
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    • 2019-12-19
    相关资源
    最近更新 更多