我已经将MVC4应用迁移到MVC6(.NET 4.6.1),并且使用内置模型验证击中了许多错误。
我有许多复杂的模型发布给控制器,除非我在配置服务下对每个模型禁用验证,否则它们与与验证无关的属性相关的不必要例外,或者只是在寄回后挂起而无需到达控制器而挂起。动作。
我已将以下行添加到我所有受影响的类的MVC配置中,但是我现在有一个需要验证的模型,因此将其关闭将导致许多代码更改。
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(TestModel)));
我尝试使用一个测试应用程序,可以复制问题:
测试控制器:
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(TestModel model)
{
return View();
}
测试模型(例如)
public class TestModel
{
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
public int NameLength
{
get
{
return Name.Length;
}
}
}
没有验证属性,该代码工作正常,但未经验证(显然)。
。但是,当发布此模型时,即使没有代码引用,也只能读取属性,而属性仅读取,并且需要它所依赖的属性,则将属于NullReference异常。此验证发生在控制器返回控制器之前。
我尝试在mvcoptions中禁用此功能,但没有任何效果:
options.MaxValidationDepth = null;
options.AllowValidatingTopLevelNodes = false;
options.AllowShortCircuitingValidationWhenNoValidatorsArePresent = true;
我不知道我是否缺少设置,但是我希望默认功能可以忽略无验证属性的属性,还是我做错了什么?
事先感谢您的帮助。
进一步@henks的建议,我添加了验证属性属性到我遇到问题的一个类的可读性属性中似乎忽略了结果:
[ValidateNever]
public Competition PrimaryCompetition
{
get
{
return GetCompetition(true);
}
}
这仍然触发一个null引用异常,因为它依赖于[必需]但未先验证的另一个属性。
我开始认为这是一个错误,而不是我的错误。
为什么发生这种情况
我还没有看到简单类型(例如在您发布的某些示例代码中),但是我们只是与复杂类型相似的问题。
通过查看源代码,这与复杂模型粘合剂的工作方式有关。它逐步介绍每个公共属性getter,它是一个复杂的类型(例如,类),无论是否使用该属性,都会发布时。我认为这可能是微软的故意选择,因为复杂类型的基本属性可能是可解决的。
例如,如果您的Competition
类和PrimaryCompetition
属性在另一个类上(此处称为Test
),则如下
public class Competition
{
public string Name { get; set; }
public List<string> Roster { get; set; } = new List<string>();
}
public class Test
{
public Competition PrimaryCompetition
{
get
{
return AllCompetitions.First();
}
}
public List<Competition> AllCompetitions { get; set; } = new List<Competition>();
}
PrimaryCompetition
的基础属性即使没有设置器,也可以修改它:
var competition = new Competition {
Name = "Soccer"
};
competition.Roster.Add("Sara");
var test = new Test();
// This code outputs "Sara"
test.AllCompetitions.Add(competition);
Console.WriteLine(test.PrimaryCompetition.Roster[0]);
// This code outputs "Amanda"
test.PrimaryCompetition.Roster[0] = "Amanda";
Console.WriteLine(test.PrimaryCompetition.Roster[0]);
可能的解决方案
- 使该属性成为方法:
public Competition PrimaryCompetition() => GetCompetition(true);
- 制作属性
internal
而不是public
:
internal Competition PrimaryCompetition
{
get
{
return GetCompetition(true);
}
}
- 将
ValidateNever
和BindNever
属性添加到属性:
[BindNever]
[ValidateNever]
public Competition PrimaryCompetition
{
get
{
return GetCompetition(true);
}
}
我们决定使用选项1,因为在微软的最佳实践中,他们建议不要从Getters Property Design中抛出例外。
属性获取器应该是简单的操作,不应有任何先决条件。如果getter可以抛出异常,则可能应该重新设计为一种方法。