ArgumentNullException 的列表框参数名称:source

发布于 2024-12-01 01:04:29 字数 1318 浏览 1 评论 0原文

设置:

我使用 MvcScaffolding 搭建了一个控制器。

对于属性 Model.IdCurrencyFrom,脚手架创建了一个 Html.DropDownListFor:

@Html.DropDownListFor(model => model.IdCurrencyFrom, 
    ((IEnumerable<FlatAdmin.Domain.Entities.Currency>)ViewBag.AllCurrencies).Select(option => new SelectListItem {
        Text = (option == null ? "None" : option.CurrencyName), 
        Value = option.CurrencyId.ToString(),
        Selected = (Model != null) && (option.CurrencyId == Model.IdCurrencyFrom)
    }), "Choose...")

无论是使用新记录还是编辑现有记录,这都可以正常工作。

问题:

只有 3 种货币,AR$、US$ 和 GB£。因此,我想要一个列表框,而不是下拉列表。

所以我将上面的内容更改为:

@Html.ListBoxFor(model => model.IdCurrencyFrom, 
    ((IEnumerable<FlatAdmin.Domain.Entities.Currency>)ViewBag.AllCurrencies).Select(option => new SelectListItem {
        Text = (option == null ? "None" : option.CurrencyName), 
        Value = option.CurrencyId.ToString(),
        Selected = (Model != null) && (option.CurrencyId == Model.IdCurrencyFrom)
    }))

我现在得到一个 ArgumentNullException,参数名称:source,但仅在编辑现有记录时。创建新记录,这很好用。

问题:

这是怎么回事?!

一切都没有改变。切换回 DropDownListFor 一切正常。切换到 ListBox(而不是 ListBoxFor),我收到错误。

该模型不为空(就像我说的,它与 DropDownListFor 配合得很好)...而且我已经没有想法了。

Setup:

I have scaffolded a controller using MvcScaffolding.

For a property, Model.IdCurrencyFrom, the scaffolding created an Html.DropDownListFor:

@Html.DropDownListFor(model => model.IdCurrencyFrom, 
    ((IEnumerable<FlatAdmin.Domain.Entities.Currency>)ViewBag.AllCurrencies).Select(option => new SelectListItem {
        Text = (option == null ? "None" : option.CurrencyName), 
        Value = option.CurrencyId.ToString(),
        Selected = (Model != null) && (option.CurrencyId == Model.IdCurrencyFrom)
    }), "Choose...")

This works fine, both with new records, or editing existing ones.

Problem:

There are only 3 currencies, AR$, US$ and GB£. So, instead of a drop down list, I want a ListBox.

So I changed the above to:

@Html.ListBoxFor(model => model.IdCurrencyFrom, 
    ((IEnumerable<FlatAdmin.Domain.Entities.Currency>)ViewBag.AllCurrencies).Select(option => new SelectListItem {
        Text = (option == null ? "None" : option.CurrencyName), 
        Value = option.CurrencyId.ToString(),
        Selected = (Model != null) && (option.CurrencyId == Model.IdCurrencyFrom)
    }))

I now get an ArgumentNullException, Parameter name: source, but only when editing an existing record. Creating new records, this works fine.

Questions:

What is happening?!

Nothing has changed. Switching back to DropDownListFor and it all works fine. Switching to ListBox (as opposed to ListBoxFor) and I get the error.

The model is not null (like I said, it works fine with the DropDownListFor)... and I've run out of ideas.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

彡翼 2024-12-08 01:04:29

我检查了 HTML 助手的源代码,这是一个有趣的练习。

TL;博士;
问题是 ListBoxFor 用于多项选择,并且它需要一个可枚举的 Model 属性。您的 Model 属性 (model.IdCurrencyFrom) 不是可枚举的,这就是您收到异常的原因。

以下是我的发现:

  1. ListBoxFor 方法将始终呈现带有 multiple="multiple" 属性的 select 元素。它是硬编码在 System.Web.Mvc.Html.SelectExtensions

    private static MvcHtmlString ListBoxHelper(HtmlHelper htmlHelper, 字符串名称, IEnumerable selectList, IDictionary htmlAttributes) {
        return SelectInternal(htmlHelper, null /* optionLabel */, name, selectList, true /* allowedMultiple */, htmlAttributes);
    }
    

    所以也许您不想允许用户使用多种货币...

  2. 当此 ListBoxHelper 尝试时,您的问题就开始了从模型属性中获取默认值:

    object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string)); 
    

    它适用于 DropDownList,因为它在调用 SelectInternal 时将 false 传递给 allowMultiple
    因为您的 ViewData.ModelState 为空(因为您的控制器之前没有进行验证),所以 defaultValue 将为 null。然后 defaultValue 使用模型的默认值进行初始化(我猜你的情况 model.IdCurrencyFromint ),所以它将是 0< /代码>。 :

    if (!usedViewData) {
            if (默认值 == null) {
                defaultValue = htmlHelper.ViewData.Eval(fullName);
            } 
     }
    

    我们已经接近异常了:)因为正如我提到的ListBoxFor仅支持多项选择,所以它尝试将defaultValue处理为IEnumbrable

    IEnumerable defaultValues = (allowMultiple) ? defaultValue 作为 IEnumerable : new[] { defaultValue };
    IEnumerable<字符串>值 = 从 defaultValues 中的对象值中选择 Convert.ToString(value, CultureInfo.CurrentCulture); 
    

    第二行出现 ArgumentException,因为 defaultValuesnull

  3. 因为它期望 defaultValue 是可枚举的,并且字符串是可枚举的。如果将 model.IdCurrencyFrom 的类型更改为 string 它将起作用。但是,当然,您将在 UI 上进行多项选择,但您只能获得模型中的第一个选择。

I've checked the source of the HTML helpers, it was a fun exercise.

TL;DR;
The problem is that ListBoxFor is for multiple selection and it expects an enumerable Model property. Your Model property (model.IdCurrencyFrom) is not an enumerable that's why you get the exception.

Here are my findings:

  1. The ListBoxFor method will render a select element with multiple="multiple" attribute, always. It is hard coded in System.Web.Mvc.Html.SelectExtensions

    private static MvcHtmlString ListBoxHelper(HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
        return SelectInternal(htmlHelper, null /* optionLabel */, name, selectList, true /* allowMultiple */, htmlAttributes);
    }
    

    So maybe you anyway don't want to allow for the user multiple currencies...

  2. Your problem starts when this ListBoxHelper tries to get the default value from your model property:

    object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string)); 
    

    It works for DropDownList because it passes false to allowMultiple when calling SelectInternal.
    Because your ViewData.ModelState is empty (because there were no validation occurred in your controller before) the defaultValue will be null. Then defaultValue gets initialized with your model's default value (your case model.IdCurrencyFrom is int I guess) so it will be 0. :

    if (!usedViewData) {
            if (defaultValue == null) {
                defaultValue = htmlHelper.ViewData.Eval(fullName);
            } 
     }
    

    We are getting close to the exception :) Because as I mentioned ListBoxFor only support multiple selection, so it tries to handle defaultValue as IEnumbrable:

    IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
    IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture); 
    

    And in the second line there is your ArgumentException because defaultValues is null.

  3. Because it expects defaultValue to be enumerable and because string is enumerable. If you change the the type of model.IdCurrencyFrom to string it will work. But of course you will have multiple selection on the UI but you will only get the first selection in your model.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文