MVC Helper,具有相同参数的方法给出不同的结果

时间:2014-04-13 09:40:11

标签: c# html asp.net-mvc-4 generics form-helpers

我正在为MVC创建一个帮助器类,并且在以不同方式传递参数“routeValues”时发现了一个问题。创建方法以默认定义某些属性。下面的代码是我用来解释我的问题的代码片段。

我有一个方法“MyBeginForm()”不接受“routeValues”的参数,“routeValues”参数直接传递给“BeginForm”方法为null。另一种方法“MyBeginForm(object routeValues)”接受“routeValues”的参数,我通过参数传递了“null”值。问题是生成的html彼此不同。

//Custom Class for custom attributes
public class MyHtmlHelper<TModel>
{
    private readonly HtmlHelper<TModel> htmlHelper;

    internal MyHtmlHelper(HtmlHelper<TModel> htmlHelper)
    {
        this.htmlHelper = htmlHelper;
    }

    //Here the routeValues parameter of Begin Form is passed directly to the method as null
    public MvcForm MyBeginForm()
    {
        var myAttributes = new Dictionary<string, object>(){
            {"test", "value"},
            {"test2", "value2"},
        };

        return htmlHelper.BeginForm("Index", "Home", null, FormMethod.Post, myAttributes);
    }

    //Here I have passed the null value through the parameter
    public MvcForm MyBeginForm(object routeValues)
    {
        var myAttributes = new Dictionary<string, object>(){
            {"test", "value"},
            {"test2", "value2"},
        };

        return htmlHelper.BeginForm("Index", "Home", routeValues, FormMethod.Post, myAttributes);
    }
}

//This class is used for static call in html
public static class MyHtmlHelperkEx
{
    public static MyHtmlHelper<TModel> MyHtmlHelper<TModel>(this HtmlHelper<TModel> htmlHelper)
    {
        return new MyHtmlHelper<TModel>(htmlHelper);
    }
}

html端使用以下代码段

<h1>Without Parameter</h1>
@using (Html.MyHtmlHelper().MyBeginForm()) { }

<h1>With parmeter</h1>
@using (Html.MyHtmlHelper().MyBeginForm(null)) { }

以下是生成的html。您可以看到属性的生成方式不同。

<h1>Without Parameter</h1>
<form action="/" method="post" test="value" test2="value2">
    System.Web.Mvc.Html.MvcForm
</form>

<h1>With parmeter</h1>
<form comparer="System.Collections.Generic.GenericEqualityComparer`1[System.String]" count="2" keys="System.Collections.Generic.Dictionary`2+KeyCollection[System.String,System.Object]" values="System.Collections.Generic.Dictionary`2+ValueCollection[System.String,System.Object]" action="/" method="post"></form>

有人可以解释为什么会这样,以及我如何解决它。

1 个答案:

答案 0 :(得分:0)

您正在混合 routeValues htmlAttributes 参数的类型。

BeginForm有2个重载需要5个参数:

  • 其中routeValues和htmlAttributes都声明为 匿名对象。请参阅msdn

  • 其中routeValues的类型为RouteValueDictionary和 htmlAttributes的类型为IDictionary<string, Object>。看到 msdn

然而,在你的助手中,你混合了这些类型。 htmlAttributes声明为字典,routeValues声明为对象。

没有参数的MyBeginForm重载是有效的,因为您传递null作为routeValues参数,因此编译器使用其他参数的类型来决定应该使用BeginForm的哪个重载。由于htmlAttributes是一个字典,它将使用我上面描述的第二个重载。 您可以快速尝试更改您的方法以强制类型为对象的null routeValues,这将强制错误的重载被选择器,您将获得相同的意外html:

return htmlHelper.BeginForm("Index", "Home", (object)null, FormMethod.Post, myAttributes);

以同样的方式,你的第二个MyBeginForm重载不能按预期工作,因为你已经声明了类型为object的routeValues,所以编译器将始终获得我上面提到的第一个重载(其中包括routeValues和htmlAttributes)被视为匿名对象。)

既然你知道会发生什么,你只需要确保选择正确的超载来解决问题。我会修复允许指定routeValues的MyBeginForm重载,然后更新不带参数的重载来调用这个(避免重复的代码)。

修复它的最简单方法是将方法中的htmlAttributes定义为匿名对象:

public MvcForm MyBeginForm(object routeValues)
{
    var myAttributes = new{
        test = "value",
        test2 = "value2",
    };
    return htmlHelper.BeginForm("Index", "Home", routeValues, FormMethod.Post, myAttributes);
}

另一个选项包括将routeValues参数声明为RouteValuesDictionary,因此您可以继续将htmlAttributes定义为字典。

public MvcForm MyBeginForm(RouteValueDictionary routeValues)
{
    var myAttributes = new Dictionary<string, object>(){
        {"test", "value"},
        {"test2", "value2"},
    };
    return htmlHelper.BeginForm("Index", "Home", routeValues, FormMethod.Post, myAttributes);
}

在这两种情况下,调用助手传递null作为参数将呈现预期的html(虽然我更喜欢使用routeValues作为匿名对象):

<form action="/" method="post" test="value" test2="value2"></form>

然后您可以像这样更新助手的第一次重载:

public MvcForm MyBeginForm()
{            
    return MyBeginForm(null);
}

现在MyBeginForm的两个重载都应该按预期工作。

希望它有所帮助!