【问题标题】:ModelState.IsValid returns true, even if required string is nullModelState.IsValid 返回 true,即使所需的字符串为 null
【发布时间】:2012-08-29 16:54:33
【问题描述】:

情况

我有一个包含三列的表的 LINQ-TO-SQL 模型。通常的 ID(身份,自动生成),然后是 int 类型的 Avarchar 类型的 B (最大)。所有列都在数据库中定义为NOT NULL

WebForms 页面上,我声明了一个绑定到项目的 DetailsView:

<asp:ValidationSummary runat="server" ShowModelStateErrors="true" />
<asp:DetailsView DataKeyNames="Id" runat="server" ItemType="test.MyTable"
    SelectMethod="..." UpdateMethod="...">
      <Fields>
        <asp:DynamicField DataField="A" />
        <asp:TemplateField>
            <EditItemTemplate>
                <asp:TextBox ID="B" runat="server" TextMode="MultiLine" Columns="80" Rows="8" Text="<%# BindItem.B %>" />
            </EditItemTemplate>
            <ItemTemplate>
                <pre><asp:Label runat="server" Text="<%# Item.B %>"  /></pre>
            </ItemTemplate>
        </asp:TemplateField>
      </Fields>
</asp:DetailsView>

所有这些代码在 ASP.NET 4.5 上的 Visual Studio 2012 中运行。

会发生什么

到目前为止一切顺利。更新和查看作品。当我输入无效值时,问题就开始了。对字段 A 使用 "" 会在页面顶部显示一个很好的错误消息。耶!但是,当我使用 "" 作为字段 B 的值时,会发生这种情况:

    public void DetailsView_UpdateItem(int id)
    {
      var item = db.MyTable.Where(row => row.Id==id).Single();
      TryUpdateModel(item);
      if (ModelState.IsValid)
        db.SubmitChanges();
    }
  1. UpdateItem 方法被调用
  2. TryUpdateModel 工作并将表单值填充到 item
  3. 调试时我可以看到 item.Bnull,正如预期的那样
  4. 然后 ModelState.IsValid 返回 true?!?
  5. db.SubmitChanges() 的调用失败并出现异常

问题

我不明白为什么当数据模型指定 B 不能为 nullModelState.IsValid 会返回 true时间>。有人可以解释一下吗?我做错了什么?

解决方法

我尝试的一件事是通过包含以下代码向数据类添加数据注释。没用。

  [MetadataType(typeof(MyTableMetadata))]
  public partial class MyTable
  {
  }

  public class MyTableMetadata
  {
    [Required]
    [StringLength(255,MinimumLength=1)]
    public string B { get; set; }
  }

很明显,我可以在 UpdateItem 方法中手动检查 null 或者我可以添加一个RequiredFieldValidator - 但我不想这样做。

【问题讨论】:

  • 我不知道您甚至被“允许”将 WebForms 与 MVC 对象和方法混合使用。生活和学习!
  • @AnnL。它被称为模型绑定,在 ASP.net 4.5 中是新的:asp.net/vnext/overview/videos/aspnet-45-web-forms-model-binding
  • 你确定过这个问题的原因吗?我遇到了同样的问题,但我已将其隔离为我为客户端 val 添加了RequiredFieldValidator。当我在页面上有此控件时,调用 ModelState.IsValid 始终返回 true,即使我在调用 ModelState.IsValid 之前手动将预期属性的值更改为 null。这可能听起来像一个奇怪的场景,但我在使用多层存储库时偶然发现了这一点。显然,标准的 Web 表单验证控件在某种程度上与模型绑定系统发生了冲突。
  • @John:最后我只是添加了一个RequiredFieldValidator,它适用于我的情况。

标签: asp.net linq-to-sql webforms model-binding


【解决方案1】:

只是一些随意的建议: 确保您引用 System.ComponentModel.DataAnnotations 3.5。 (不是 3.6)或其他一些正确的命名空间(据我所知,有一些仅用于 WCF RIA 服务)。 尝试不同的设置 [DisplayFormat(ConvertEmptyStringToNull = ... )] 确保您正在执行 POST 请求 =)

【讨论】:

  • 我引用的是 4.0 版。是好是坏?使用 ConvertEmptyStringToNull 没有任何区别。我可以确认我正在执行 POST 请求。使用 Fiddler 和 Chrome 进行检查。
  • 据我了解,您有一个模型 = MyTable 的强类型视图?
  • 我没有使用 MVC。这是常规的 WebForms。对问题进行了一点澄清以使其清楚。很抱歉造成混乱。
【解决方案2】:

为什么你认为你得到一个空值?空字符串不等同于 null。您可以按照您的建议通过手动检查 null 来确认这一点。

除了非空约束之外,您还可以添加StringLengthAttribute 来强制执行非空约束。

【讨论】:

  • 看来(根据stackoverflow.com/questions/7820815/…)我需要这两个属性。试过了,但没有任何改变。我想我得到了一个空值,因为这是调试器告诉我的。在 TryModelUpdate 调用后检查 item.B 显示它为空。
  • @Simon,您是否将MinimumLength 属性设置为StringLengthAttribute?您能否更新代码示例以准确反映您当前的情况?
  • @Simon,在您的代码示例中,第二个 MyTable 类声明实际上应该是 MyTableMetadata 吗?如果没有,MetadataTypeAttribute 的存在可能会抑制约束的传播。
  • @Simon,另外,TryUpdateModel 的返回值是什么,当你把它传递给它时?
  • 你是对的:该类应该被称为MetadataTypeAttribute,我相应地更改了代码并添加了StringLengthAttributeTryUpdateModel 返回true。 (注:如果你看这个方法的代码,它以return IsValid;结尾。)
【解决方案3】:

TryUpdateModel() 方法本质上(简化了很多)使用模型类中的字段/属性的名称来在帖子的表单集合中查找发布的值。在您为文本框指定 ID = B 的 asp.net 网格的情况下,控件的名称变为类似于 ctl00$B 的名称。模型绑定器将无法连接这些点并从表单集合中获取值,即使它存在于帖子中。

我觉得为您的可编辑字段使用“asp:BoundField”可能会解决您的问题。您也可以考虑编写自定义模型绑定器或自己分配值。更改模型中属性的名称以匹配生成的控件的名称是解决问题的另一种方法(请不要选择此选项。)

TryUpdateModel() 在找到帖子中的值但无法将值转换为模型中的类型时会返回 false。否则返回真。

【讨论】:

  • 感谢您精心编写的答案。但我不认为你理解原来的问题。当我输入一些文本时,一切正常。由此我得出结论,TryUpdateModel() 按预期工作并且可以找到控件。这也是意料之中的,因为模型绑定就是为了做到这一点而设计的。但我还是尝试使用BoundField,但没有任何区别。
【解决方案4】:

公共类 SecurityLayer { StringBuilder SB = new StringBuilder();

    public string SecurityValidate(object OBJ)
    {
        SB.Clear();
        var context = new ValidationContext(OBJ, serviceProvider: null, items: null);
        var results = new List<ValidationResult>();

        var isValid = Validator.TryValidateObject(OBJ, context, results);

        if (!isValid)
        {
            foreach (var validationResult in results)
            {
                //    Console.WriteLine(validationResult.ErrorMessage);
                SB.AppendLine(validationResult.ErrorMessage);
            }
        }

        return (SB.Length == 0 ? "SUCCESS" : SB.ToString());
    }
}

【讨论】:

    猜你喜欢
    • 2022-09-25
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 2015-02-12
    • 2013-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多