英文:
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 = "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();
}
答案1
得分: 1
你可以查看与非可空引用类型和[Required]属性相关的文档。
在服务器端,如果属性为null,则必填值被视为缺失。非可空字段始终有效,并且不会显示[Required]属性的错误消息。
但是,非可空属性的模型绑定可能会失败,导致出现类似于“值''无效”的错误消息。
当你提交表单时,如果在输入字段中没有设置任何值,你会看到一个键值对key-""
,但如果你没有选择下拉列表的任何选项,你将看不到键值对。
我尝试移除隐藏输入中的value属性,禁用客户端验证并提交表单:
<input type="hidden" asp-for="ProductsProductType.ProductId" />
然后由于模型绑定失败而得到模型状态错误(如果你想避免这个错误,可以创建一个带有int? xxId的视图模型):
错误信息为:
在尝试保存更改时,'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-""
,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:
<input type="hidden" asp-for="ProductsProductType.ProductId" />
and get the modelstate err due to failed model binding(if you want to avoid it you may create a viewmodel with int? xxId):
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["ProductTypes"] = 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("_ValidationScriptsPartial");}
}
in _ValidationScriptsPartial
:
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
if you still can't enable the client validation,please show us a minimal example of your current page and layout view
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论