2 回答
TA贡献1811条经验 获得超5个赞
在我看来,在模型中包含数据库请求并不是那么干净
我倾向于同意,但为了验证,我倾向于破例。为什么?
当它决定通过验证来完成IValidatableObject它应该是验证的唯一点尽可能(我的意思是,该Validate方法不适合应用涉及其他实体更复杂的验证的最佳场所)。
将“简单”验证规则的一部分放在实体本身中,将另一部分放在 --well 中,无论在哪里都非常不方便。这就是问题所在:它可以在任何地方。因此,当通过另一个代码路径保存实体时可以跳过它。
因此,我倾向于将执行验证的上下文提供给validationContext. 这是在ValidateEntity由SaveChanges(when context.Configuration.ValidateOnSaveEnabledis true)调用的上下文方法的覆盖中完成的:
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
items.Add("context", this);
return base.ValidateEntity(entityEntry, items);
}
现在可以获取Validate方法内部上下文的句柄:
var context = (MyContext)validationContext.Items["context"];
...它可用于执行数据库查询。
不过,这应该小心处理。遵守三个规则是好的:
不要运行更改更改跟踪器内容的查询,即不要查询完整实体,只查询投影。在许多被跟踪实体等待保存到数据库时进行验证。最好不要更改此跟踪实体集合中的任何内容。
不要运行繁重的查询。
当应用程序经常在一个工作单元中保存大量实体时,不要这样做。(在这种情况下,无论如何使用IValidatableObject都不是最好的主意)。
有了这个,就可以运行如下验证:
var ids = externalClasses.Select(c => c.ID).ToList();
if (context.ExternalClasses.Any(c => !ids.Contains(c.ID))
{
yield return new ValidationResult("Some external classes don't exist",
new[] { nameof(externalClasses) });
}
这将执行一个相对较轻的查询,该查询仅返回一个布尔值并且不会将新实体附加到更改跟踪器。
TA贡献1780条经验 获得超1个赞
到目前为止,我解决了以下问题:
我按如下方式创建了实体类(通过使用单个外部对象而不是 ICollection 来简化示例):
[Table("xyz")]
public partial class TestClass{
[Key]
public int key {get; set;}
[ForeignKey("key")]
[Required]
public virtual ExternalClass externalClass{get; set;}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
...
}
}
然后在我ServiceClass创建新记录的地方:
public void InsertNewRecord(TestClass newClass){
newClass.externalClass = context.Where(e => e.ID = newClass.externalClass.Id).First();
context.Add(newClass);
context.SaveChanges();
}
如果无法找到 externalClass(外部),newClass.externalClass则将其设置为 null 并由于[Required]注解而引发验证错误。这有一个额外的好处,即 EF 不会尝试保存外来对象,externalClass因为它现在识别出该条目仍然存在。
- 2 回答
- 0 关注
- 180 浏览
添加回答
举报