所有导航属性的 ModelState 错误
ModelState Errors for all Navigation Properties
我的 WebApi 和 ModelState 有问题。每当我向 API 发送数据时,它都会在所有导航属性上引发 ModelState 错误。这是我的模型:
public class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int StandardId { get; set; }
public Standard Standard { get; set; }
}
public class Standard
{
public int StandardId { get; set; }
[Required]
public string StandardName { get; set; }
public ICollection<Student> Students { get; set; }
}
如您所见,我没有分配 virtual 关键字,这应该不是问题,因为我不想延迟加载。
这是我的 API:
[HttpPut, Route("updateStudent/{id:int}")]
public IHttpActionResult Put(int id, Student student)
{
// ModelState throws an error here!!
if (ModelState.IsValid && id == student.StudentId) {
...
}
}
我的请求是这样的:
{
"StudenID": 0,
"StudentName": "Tom",
"StandardId": 1
}
当我检查模型到达 api 时的样子时,我可以看到所有数据都已填充并且它基本上用新的标准实例替换了标准 属性。但是,我当然不希望它抛出验证错误。
编辑:
它抛出错误,指出 StandardName 属性 是必需的。
显然这是导航的一部分 属性。我不想检查导航 属性 是否有错误。
您应该创建一个新模型,它应该只包含那些将作为输入发布的项目,并在控制器操作中与您的数据模型进行通信。您可以在您的案例中创建一个 ViewModel,例如:
public class StudentViewModel
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int StandardId { get; set; }
}
并相应地更改操作方法参数。
[HttpPut, Route("updateStudent/{id:int}")]
public IHttpActionResult Put(int id, StudentViewModel student)
{
if (ModelState.IsValid && id == student.StudentId) {
...
// map with your Student Entity here as per your needs
}
}
对于目前的解决方法,您可以从 ModelState
:
中删除那些 Standard
实体属性
public IHttpActionResult Put(int id, Student student)
{
// ignore StandardName property
ModelState.Remove(nameof(student.Standard.StandardName));
if (ModelState.IsValid && id == student.StudentId) {
...
}
向模型 属性 添加 [Required] 属性将对其进行验证。删除它将解决问题。但是,如果您无法更改它,就像它来自您无法在解决方案中修改的导入 class DLL 一样,请尝试为您的请求模型创建一个单独的模型,其中 StandardName 属性 没有 [Required]属性。
我找到了解决方法。我遇到了简单模型 classes.The Create
CRUD 页面总是失败的问题,因为 Enrollments
总是 null
.
public class Course
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
[Display(Name = "Is Teacher?")]
public bool IsTeacher { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
public class Enrollment
{
public int ID { get; set; }
public int CourseID { get; set; }
public int PersonID { get; set; }
public Course Course { get; set; }
public Person Person { get; set; }
}
- 对于
Course
和 Person
,Create
CRUD 页面总是失败,因为 Enrollments
总是 null
。这个问题有一个简单的解决方法:改变
public ICollection<Enrollment> Enrollments { get; set; }
至
public ICollection<Enrollment> Enrollments { get; set; } = new List<Enrollment>();
- 我遇到的第二个问题是注册。我试过改变
public Course Course { get; set; }
public Person Person { get; set; }
至
public Course Course { get; set; } => new() { Name = "" };
public Person Person { get; set; } => new() { Name = "" };
这不起作用,因为它只会将空白条目添加到课程和人员列表中。这告诉我它使用的是 Course 和 Person 字段,而不是 CourseID 和 PersonID 字段。解决方案是添加 ID。
最终解决问题的方法是将其更改为:
public Course Course { get => _course == null && CourseID != default ? new() { ID = CourseID, Name = "" } : _course; set => _course = value; }
public Person Person { get => _person == null && PersonID != default ? new() { ID = PersonID, Name = "" } : _person; set => _person = value; }
Course _course; Person _person;
这使得 Index
和 Create
页面都可以正常工作。
最终代码如下:
public class Course
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Enrollment> Enrollments { get; set; } = new List<Enrollment>();
}
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
[Display(Name = "Is Teacher?")]
public bool IsTeacher { get; set; }
public ICollection<Enrollment> Enrollments { get; set; } = new List<Enrollment>();
}
public class Enrollment
{
public int ID { get; set; }
public int CourseID { get; set; }
public int PersonID { get; set; }
public Course Course { get => _course == null && CourseID != default ? new() { ID = CourseID, Name = "" } : _course; set => _course = value; }
public Person Person { get => _person == null && PersonID != default ? new() { ID = PersonID, Name = "" } : _person; set => _person = value; }
Course _course; Person _person;
}
总而言之,要制作导航属性,请遵循以下指南:
有了列表,你可以安全地将它设置为空。
对于一对一的关系,它更复杂,看起来应该是这样的:
public int FieldID { get; set; }
public FieldType Field
{
get => _field == null && FieldID != default ? new() { ID = FieldID, ... } : _field;
set => _field = value;
}
FieldType _field;
其中 ...
只是将所有可为空的字段设置为非空值以确保 ModelState.IsValid
为真。
我个人不同意关于模型视图的逻辑类。每次我修改数据库的功能(例如字段名称)时都遵循这个逻辑,我必须手动更改所有模型视图 类 以匹配。我理解关于 POST 攻击的说法,但我指的是可以修改相关数据集的所有字段的情况。通常,我只会将这些 CRUD 模型的访问权限授予相关数据的管理员。
我的 WebApi 和 ModelState 有问题。每当我向 API 发送数据时,它都会在所有导航属性上引发 ModelState 错误。这是我的模型:
public class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int StandardId { get; set; }
public Standard Standard { get; set; }
}
public class Standard
{
public int StandardId { get; set; }
[Required]
public string StandardName { get; set; }
public ICollection<Student> Students { get; set; }
}
如您所见,我没有分配 virtual 关键字,这应该不是问题,因为我不想延迟加载。
这是我的 API:
[HttpPut, Route("updateStudent/{id:int}")]
public IHttpActionResult Put(int id, Student student)
{
// ModelState throws an error here!!
if (ModelState.IsValid && id == student.StudentId) {
...
}
}
我的请求是这样的:
{
"StudenID": 0,
"StudentName": "Tom",
"StandardId": 1
}
当我检查模型到达 api 时的样子时,我可以看到所有数据都已填充并且它基本上用新的标准实例替换了标准 属性。但是,我当然不希望它抛出验证错误。
编辑: 它抛出错误,指出 StandardName 属性 是必需的。 显然这是导航的一部分 属性。我不想检查导航 属性 是否有错误。
您应该创建一个新模型,它应该只包含那些将作为输入发布的项目,并在控制器操作中与您的数据模型进行通信。您可以在您的案例中创建一个 ViewModel,例如:
public class StudentViewModel
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int StandardId { get; set; }
}
并相应地更改操作方法参数。
[HttpPut, Route("updateStudent/{id:int}")]
public IHttpActionResult Put(int id, StudentViewModel student)
{
if (ModelState.IsValid && id == student.StudentId) {
...
// map with your Student Entity here as per your needs
}
}
对于目前的解决方法,您可以从 ModelState
:
Standard
实体属性
public IHttpActionResult Put(int id, Student student)
{
// ignore StandardName property
ModelState.Remove(nameof(student.Standard.StandardName));
if (ModelState.IsValid && id == student.StudentId) {
...
}
向模型 属性 添加 [Required] 属性将对其进行验证。删除它将解决问题。但是,如果您无法更改它,就像它来自您无法在解决方案中修改的导入 class DLL 一样,请尝试为您的请求模型创建一个单独的模型,其中 StandardName 属性 没有 [Required]属性。
我找到了解决方法。我遇到了简单模型 classes.The Create
CRUD 页面总是失败的问题,因为 Enrollments
总是 null
.
public class Course
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
[Display(Name = "Is Teacher?")]
public bool IsTeacher { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
public class Enrollment
{
public int ID { get; set; }
public int CourseID { get; set; }
public int PersonID { get; set; }
public Course Course { get; set; }
public Person Person { get; set; }
}
- 对于
Course
和Person
,Create
CRUD 页面总是失败,因为Enrollments
总是null
。这个问题有一个简单的解决方法:改变
public ICollection<Enrollment> Enrollments { get; set; }
至
public ICollection<Enrollment> Enrollments { get; set; } = new List<Enrollment>();
- 我遇到的第二个问题是注册。我试过改变
public Course Course { get; set; }
public Person Person { get; set; }
至
public Course Course { get; set; } => new() { Name = "" };
public Person Person { get; set; } => new() { Name = "" };
这不起作用,因为它只会将空白条目添加到课程和人员列表中。这告诉我它使用的是 Course 和 Person 字段,而不是 CourseID 和 PersonID 字段。解决方案是添加 ID。
最终解决问题的方法是将其更改为:
public Course Course { get => _course == null && CourseID != default ? new() { ID = CourseID, Name = "" } : _course; set => _course = value; }
public Person Person { get => _person == null && PersonID != default ? new() { ID = PersonID, Name = "" } : _person; set => _person = value; }
Course _course; Person _person;
这使得 Index
和 Create
页面都可以正常工作。
最终代码如下:
public class Course
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Enrollment> Enrollments { get; set; } = new List<Enrollment>();
}
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
[Display(Name = "Is Teacher?")]
public bool IsTeacher { get; set; }
public ICollection<Enrollment> Enrollments { get; set; } = new List<Enrollment>();
}
public class Enrollment
{
public int ID { get; set; }
public int CourseID { get; set; }
public int PersonID { get; set; }
public Course Course { get => _course == null && CourseID != default ? new() { ID = CourseID, Name = "" } : _course; set => _course = value; }
public Person Person { get => _person == null && PersonID != default ? new() { ID = PersonID, Name = "" } : _person; set => _person = value; }
Course _course; Person _person;
}
总而言之,要制作导航属性,请遵循以下指南:
有了列表,你可以安全地将它设置为空。
对于一对一的关系,它更复杂,看起来应该是这样的:
public int FieldID { get; set; }
public FieldType Field
{
get => _field == null && FieldID != default ? new() { ID = FieldID, ... } : _field;
set => _field = value;
}
FieldType _field;
其中 ...
只是将所有可为空的字段设置为非空值以确保 ModelState.IsValid
为真。
我个人不同意关于模型视图的逻辑类。每次我修改数据库的功能(例如字段名称)时都遵循这个逻辑,我必须手动更改所有模型视图 类 以匹配。我理解关于 POST 攻击的说法,但我指的是可以修改相关数据集的所有字段的情况。通常,我只会将这些 CRUD 模型的访问权限授予相关数据的管理员。