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();
}
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