无法强制要求List <int>

时间:2017-07-19 21:42:20

标签: c# asp.net asp.net-mvc entity-framework

我正在尝试使用数据注释将验证添加到模型中不能为空的List中。我尝试了多种自定义属性,包括herehere

我的观点:

<div class="form-group">
    @* Model has a list of ints, LocationIDs *@
    @Html.LabelFor(model => model.LocationIDs, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        <select class="select2 form-control" multiple id="LocationIDs" name="LocationIDs">
            @* Adds every possible option to select box *@
            @foreach (LocationModel loc in db.Locations)
            {
                <option value="@loc.ID">@loc.Name</option>
            }
        </select>
        @Html.ValidationMessageFor(model => model.LocationIDs, "", new { @class = "text-danger" })
    </div>
</div>

型号:

public class ClientModel
{
    public int ID { get; set; }
    [Required] // Does nothing
    public List<int> LocationIDs { get; set; }
}

控制器:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,LocationIDs")] ClientModel clientModel)
{
    if (ModelState.IsValid)
    {
        db.Clients.Add(clientModel);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(clientModel);
}

我尝试过的(功能相同)属性之一:

[AttributeUsage(AttributeTargets.Property)]
public sealed class CannotBeEmptyAttribute : RequiredAttribute
{
    public override bool IsValid(object value)
    {
        var list = value as IEnumerable;
        return list != null && list.GetEnumerator().MoveNext();
    }
}

目前,即使没有选择任何内容,检查null或空列表也会通过验证。在这种情况下,绑定包含第一个选项的长度为1的列表。

我已经确认控制器实际上是List长度的一个。但是,我不确定如何改变这种行为。我仍然认为这可能是下面的块引用中描述的内容。

认为 我的问题可能会在this answer's edit中说明,但我不确定如何解决它。

摘录如下:

  

您还必须小心如何在视图中绑定列表。   例如,如果将List绑定到这样的视图:

<input name="ListName[0]" type="text" />
<input name="ListName[1]" type="text" />
<input name="ListName[2]" type="text" />
<input name="ListName[3]" type="text" />
<input name="ListName[4]" type="text" /> 
  

MVC模型绑定器将始终放入5个元素   list,所有String.Empty。如果这是您的View的工作方式,那么您的属性   需要更复杂一些,比如使用Reflection来拉   泛型类型参数并将每个列表元素与之比较   默认(T)或其他。

3 个答案:

答案 0 :(得分:1)

您可以尝试使用Type构建验证器,并验证列表中的任何itens是否与您的类型的默认值不同。 更改您提到的示例here

public class CannotBeEmptyAttribute : ValidationAttribute
{
    private const string defaultError = "'{0}' must have at least one element.";

    public Type ListType { get; private set; }

    protected CannotBeEmptyAttribute(Type listType) : base(defaultError)
    {
        this.ListType = listType;
    }

    public override bool IsValid(object value)
    {
        object defaultValue = ListType.IsValueType ? Activator.CreateInstance(ListType) : null;

        IEnumerable list = value as IEnumerable;

        if (list != null)
        {
            foreach (var item in list)
            {
                if(item != defaultValue)
                {
                    return true;
                }
            }
        }
        return false;            
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(this.ErrorMessageString, name);
    }
}

答案 1 :(得分:1)

您的CannotBeEmptyAttribute没问题。我使用了完全相同的代码,但效果很好。请务必更改您的视图模型以使用它(而不是在这种情况下不会执行您想要的Required

除非您实施,否则您的自定义属性不会为您提供客户端验证。这意味着即使表单无效,表单也会发布,但if (ModelState.IsValid)会抓住它。您是否使用调试器查看IsValid是否为false

&#34;手动&#34;创建输入对于服务器端验证来说是完全正确的,但客户端不会工作,因为缺少必要的data-属性。

这是一个表单的最小版本,其中包含手动创建的多选输入,可与CannotBeEmptyAttribute一起使用:

@using (Html.BeginForm("TestPost", "Home"))
{
    <select multiple="multiple" name="TestList">
        <option value="1">One</option>
        <option value="2">Two</option>
        <option value="3">Three</option>
    </select>
    @Html.ValidationMessage("TestList")
    @Html.ValidationSummary()

    <input type="submit" value="Save"/>
}

答案 2 :(得分:0)

我能够实现null实际绑定的零选择的唯一方法是使用Html.ListBoxFor(我最初无法弄清楚 - 它应该是通过这种方式完成的开始):

@Html.ListBoxFor(model => model.SelectedLocations, Model.AllLocations, new { @class = "select2 form-control" })

我还没有通过客户端验证工作,但我会将其作为另一个问题发布。