ModelState未对必填字段进行验证

huangapple go评论87阅读模式
英文:

Required Field not being validated by ModelState

问题

我有一个[Products]类,一个[Product_Types]类和一个[Products_Product_Types]类。我有一个用于[Products_Product_Types]的Razor页面,但是ModelState只验证了两个[Required] ID属性中的一个...具体来说,它验证了ProductProductType.ProductId属性,而忽略了ProductProductType.ProductTypeId属性。在下面的代码示例中,当我将ProductProductType.ProductTypeId留空时,会出现以下错误...

尝试保存更改时,'ProductProductType.ProductTypeId'的值未知。这是因为该属性也是一个外键的一部分,而关系中的主体实体未知。

...而我期望看到的是ProductProductType.ProductTypeId下拉列表的UnobtrusiveValidation,即必填项。

我完全预料到问题出在我的DatabaseContext.cs文件中,但我已经查阅了所有我能找到的文档,但不确定如何解决这个错误。

Product.cs

public partial class Product
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ProductId { get; set; }

    [Display(Name = "Product Name")]
    public string? ProductName { get; set; }
}

ProductType.cs

public partial class ProductType
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ProductTypeId { get; set; }

    [Display(Name = "Product Type")]
    public string? ProductTypeName { get; set; }
}

ProductsProductType.cs

public partial class ProductsProductType
{
    [Required]
    public int ProductId { get; set; }

    [Required]
    [Display(Name = "Product Type")]
    public int ProductTypeId { get; set; }

    public virtual Product? Product { get; set; } = null!;
    //public virtual ICollection<Product>? Products { get; set; } = new List<Product>();

    public virtual ProductType? ProductType { get; set; } = null!;
    //public virtual ICollection<ProductType>? ProductTypes { get; set; } = new List<ProductType>();
}

DatabaseContext.cs

public virtual DbSet<ProductsProductType> ProductsProductTypes { get; set; }

modelBuilder.Entity<ProductsProductType>(entity =>
{
    entity.HasKey(e => new { e.ProductId, e.ProductTypeId }).HasName("PK_Products_Product_Types");

    entity.ToTable("Products_Product_Types");

    entity.Property(e => e.ProductId).HasColumnName("ProductID");
    entity.Property(e => e.ProductTypeID).HasColumnName("ProductTypeID");

    entity.HasOne(f => f.Product).WithMany(e => e.ProductsProductTypes)
        .HasForeignKey(f => f.ProductId).IsRequired()
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("FK_Products_Product_Types_Products");

    entity.HasOne(f => f.ProductType).WithMany(e => e.ProductsProductTypes)
        .HasForeignKey(f => f.ProductTypeID).IsRequired()
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("FK_Product_Product_Types_Product_Types");
});

/ProductsProductTypes/Create.cshtml

<form method="post" asp-page-handler="ProductProductTypeCreate" data-ajax="true" data-ajax-method="post" data-ajax-success="refreshPartial('GetProductProductTypes', 'ProductProductTypes-container')" data-ajax-failure="ajaxValidationHandler">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="ProductProductType.ProductId" value="@ViewBag.ProductId" />
    <div class="form-group">
        <label asp-for="ProductProductType.ProductTypeId" class="control-label"></label>
        <select asp-for="ProductProductType.ProductTypeId" class="form-control" asp-items="ViewBag.ProductTypes"><option disabled selected>- Select Product Type -</option></select>
        <span asp-validation-for="ProductProductType.ProductTypeId" class="text-danger"></span>
    </div>
    <div class="form-group">
        <input type="submit" value="Save" class="btn btn-primary" />
        <a class="btn btn-secondary" href="javascript:parent.actionPaneClose();">Cancel</a>
    </div>
</form>

/ProductsProductTypes/Create.cshtml.cs

[BindProperty]
public ProductsProductType ProductsProductType { get; set; } = default!;

public async Task<IActionResult> OnPostProductProductTypeCreateAsync()
{
    if (!ModelState.IsValid || _context.ProductsProductTypes == null || ProductsProductType == null)
    {
        ViewData["ProductId"] = ProductsProductType.ProductId;
        ViewData["ProductTypes"] = new SelectList(_context.ProductTypes, nameof(ProductType.ProductTypeId), nameof(ProductType.ProductTypeName));
        return Page();
    }

    _context.ProductsProductTypes.Add(ProductsProductType);
    await _context.SaveChangesAsync();

    return Page();
}
英文:

I have a [Products] class, a [Product_Types] class and a [Products_Product_Types] class. I have a Razor page for [Products_Product_Types] and ModelState is only validating one of the two [Required] ID properties...specifically it's validating the ProductProductType.ProductId property and ignoring the ProductProductType.ProductTypeId property. In the code samples below, when I leave the ProductProductType.ProductTypeId blank I get this error...

> The value of 'ProductProductType.ProductTypeId' is unknown when
> attempting to save changes. This is because the property is also part
> of a foreign key for which the principal entity in the relationship is
> not known.

...when instead I expect to see the UnobtrusiveValidation that the ProductProductType.ProductTypeId dropdown is required.

I fully expect the issue is in my DatabaseContext.cs file, but I've reviewed all the documentation I can find and I'm not sure how to resolve this error.

Product.cs

public partial class Product
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ProductId { get; set; }

    [Display(Name = &quot;Product Name&quot;)]
    public string? ProductName { get; set; }
}

ProductType.cs

public partial class ProductType
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ProductTypeId { get; set; }

    [Display(Name = &quot;Product Type&quot;)]
    public string? ProductTypeName { get; set; }
}

ProductsProductType.cs

public partial class ProductsProductType
{
    [Required]
    public int ProductId { get; set; }

    [Required]
    [Display(Name = &quot;Product Type&quot;)]
    public int ProductTypeId { get; set; }

    public virtual Product? Product { get; set; } = null!;
    //public virtual ICollection&lt;Product&gt;? Products { get; set; } = new List&lt;Product&gt;();

    public virtual ProductType? ProductType { get; set; } = null!;
    //public virtual ICollection&lt;ProductType&gt;? ProductTypes { get; set; } = new List&lt;ProductType&gt;();
}

DatabaseContext.cs

public virtual DbSet&lt;ProductsProductType&gt; ProductsProductTypes{ get; set; }

modelBuilder.Entity&lt;ProductsProductType&gt;(entity =&gt;
{
    entity.HasKey(e =&gt; new { e.ProductId, e.ProductTypeId }).HasName(&quot;PK_Products_Product_Types&quot;);

    entity.ToTable(&quot;Products_Product_Types&quot;);

    entity.Property(e =&gt; e.ProductId).HasColumnName(&quot;ProductID&quot;);
    entity.Property(e =&gt; e.ProductTypeID).HasColumnName(&quot;ProductTypeID&quot;);

    entity.HasOne(f =&gt; f.Product).WithMany(e =&gt; e.ProductsProductTypes)
        .HasForeignKey(f =&gt; f.ProductId).IsRequired()
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName(&quot;FK_Products_Product_Types_Products&quot;);

    entity.HasOne(f =&gt; f.ProductType).WithMany(e =&gt; e.ProductsProductTypes)
        .HasForeignKey(f =&gt; f.ProductTypeID).IsRequired()
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName(&quot;FK_Product_Product_Types_Product_Types&quot;);
});

/ProductsProductTypes/Create.cshtml

&lt;form method=&quot;post&quot; asp-page-handler=&quot;ProductProductTypeCreate&quot; data-ajax=&quot;true&quot; data-ajax-method=&quot;post&quot; data-ajax-success=&quot;refreshPartial(&#39;GetProductProductTypes&#39;, &#39;ProductProductTypes-container&#39;)&quot; data-ajax-failure=&quot;ajaxValidationHandler&quot;&gt;
            &lt;div asp-validation-summary=&quot;ModelOnly&quot; class=&quot;text-danger&quot;&gt;&lt;/div&gt;
            &lt;input type=&quot;hidden&quot; asp-for=&quot;ProductProductType.ProductId&quot; value=&quot;@ViewBag.ProductId&quot; /&gt;
            &lt;div class=&quot;form-group&quot;&gt;
                &lt;label asp-for=&quot;ProductProductType.ProductTypeId&quot; class=&quot;control-label&quot;&gt;&lt;/label&gt;
                &lt;select asp-for=&quot;ProductProductType.ProductTypeId&quot; class=&quot;form-control&quot; asp-items=&quot;ViewBag.ProductTypes&quot;&gt;&lt;option disabled selected&gt;- Select Product Type -&lt;/option&gt;&lt;/select&gt;
                &lt;span asp-validation-for=&quot;ProductProductType.ProductTypeId&quot; class=&quot;text-danger&quot;&gt;&lt;/span&gt;
            &lt;/div&gt;
            &lt;div class=&quot;form-group&quot;&gt;
                &lt;input type=&quot;submit&quot; value=&quot;Save&quot; class=&quot;btn btn-primary&quot; /&gt;
                &lt;a class=&quot;btn btn-secondary&quot; href=&quot;javascript:parent.actionPaneClose();&quot;&gt;Cancel&lt;/a&gt;
            &lt;/div&gt;
        &lt;/form&gt;

/ProductsProductTypes/Create.cshtml.cs

[BindProperty]
public ProductsProductType ProductsProductType { get; set; } = default!;

public async Task&lt;IActionResult&gt; OnPostProductProductTypeCreateAsync()
{
    if (!ModelState.IsValid || _context.ProductsProductTypes == null || ProductsProductType == null)
    {
        ViewData[&quot;ProductId&quot;] = ProductsProductType.ProductId;
        ViewData[&quot;ProductTypes&quot;] = new SelectList(_context.ProductTypes, nameof(ProductType.ProductTypeId), nameof(ProductType.ProductTypeName));
        return Page();
    }

    _context.ProductsProductTypes.Add(ProductsProductType);
    await _context.SaveChangesAsync();

    return Page();
}

答案1

得分: 1

你可以查看与非可空引用类型和[Required]属性相关的文档

在服务器端,如果属性为null,则必填值被视为缺失。非可空字段始终有效,并且不会显示[Required]属性的错误消息。

但是,非可空属性的模型绑定可能会失败,导致出现类似于“值''无效”的错误消息。

当你提交表单时,如果在输入字段中没有设置任何值,你会看到一个键值对key-"",但如果你没有选择下拉列表的任何选项,你将看不到键值对。

我尝试移除隐藏输入中的value属性,禁用客户端验证并提交表单:

<input type="hidden" asp-for="ProductsProductType.ProductId" />

ModelState未对必填字段进行验证

然后由于模型绑定失败而得到模型状态错误(如果你想避免这个错误,可以创建一个带有int? xxId的视图模型):

ModelState未对必填字段进行验证

错误信息为:

在尝试保存更改时,'ProductProductType.ProductTypeId'的值未知。这是因为该属性还是一个外键的一部分,而关系中的主体实体未知。

要求你发送与ProductType实体的主键匹配的ProductTypeId。

由于你已经从数据库中读取了所有选项:

ViewData["ProductTypes"] = new SelectList(_context.ProductTypes, nameof(ProductType.ProductTypeId), nameof(ProductType.ProductTypeName));

请确保你已经选择了其中一个选项并启用了客户端验证,例如:

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

_ValidationScriptsPartial中:

<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

如果你仍然无法启用客户端验证,请向我们展示你当前页面和布局视图的最小示例。

英文:

You could check the document related with Non-nullable reference types and the [Required] attribute

> On the server, a required value is considered missing if the property
> is null. A non-nullable field is always valid, and the [Required]
> attribute's error message is never displayed.
>
> However, model binding for a non-nullable property may fail, resulting
> in an error message such as The value '' is invalid.

When you submit the form if you don't set any value in input field,you would see a key-value pair key-&quot;&quot;,but if you don't select any option of the dropdown,you won't see the key-value pair

I tried to remove value attribute in your hidden input,disable client validation and submit the form:

&lt;input type=&quot;hidden&quot; asp-for=&quot;ProductsProductType.ProductId&quot;  /&gt;

ModelState未对必填字段进行验证

and get the modelstate err due to failed model binding(if you want to avoid it you may create a viewmodel with int? xxId):

ModelState未对必填字段进行验证

The error

> The value of 'ProductProductType.ProductTypeId' is unknown when
> attempting to save changes. This is because the property is also part
> of a foreign key for which the principal entity in the relationship is
> not known.

ask you to send the ProductTypeId matches ProductType entity's PK in db

Since you've read all your options from db

ViewData[&quot;ProductTypes&quot;] = new SelectList(_context.ProductTypes, nameof(ProductType.ProductTypeId), nameof(ProductType.ProductTypeName));

make sure you've selected one of the options and enabled client validation

like:

@section Scripts {
    @{await Html.RenderPartialAsync(&quot;_ValidationScriptsPartial&quot;);}
}

in _ValidationScriptsPartial:

&lt;script src=&quot;~/lib/jquery-validation/dist/jquery.validate.min.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js&quot;&gt;&lt;/script&gt;

if you still can't enable the client validation,please show us a minimal example of your current page and layout view

huangapple
  • 本文由 发表于 2023年8月8日 21:54:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76860229.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定