实体框架 - 外键验证

具有以下内容TestClass:


[Table("xyz")]

public partial class TestClass{

    [Key]

    public int key {get; set;}

    [ForeignKey("key")]

    public virtual ICollection<ExternalClass> externalClasses{get; set;}


    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)

    {

        ... 

    }

}

我需要如何设置该类以满足我的以下要求:

  • 我想用实体框架单元测试来测试模型

  • 在生产中,externalClasses不应保存(应该已经在数据库中)

  • EF 必须确保每个外部类都存在于 DB 中。如果没有,则抛出异常。

我的第一个想法是将外部对象设置为 null 并在验证方法中请求数据库以检查外键是否存在。但是这种方法在单元测试中效果不佳,除此之外,在我看来,在模型中包含数据库请求并不是那么干净。

有没有人知道如何使用 EF 以干净的方式处理这个问题?


MM们
浏览 181回答 2
2回答

四季花海

在我看来,在模型中包含数据库请求并不是那么干净我倾向于同意,但为了验证,我倾向于破例。为什么?当它决定通过验证来完成IValidatableObject它应该是验证的唯一点尽可能(我的意思是,该Validate方法不适合应用涉及其他实体更复杂的验证的最佳场所)。将“简单”验证规则的一部分放在实体本身中,将另一部分放在 --well 中,无论在哪里都非常不方便。这就是问题所在:它可以在任何地方。因此,当通过另一个代码路径保存实体时可以跳过它。因此,我倾向于将执行验证的上下文提供给validationContext. 这是在ValidateEntity由SaveChanges(when context.Configuration.ValidateOnSaveEnabledis true)调用的上下文方法的覆盖中完成的:protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items){&nbsp; &nbsp; items.Add("context", this);&nbsp; &nbsp; 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)){&nbsp; &nbsp; yield return new ValidationResult("Some external classes don't exist",&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; new[] { nameof(externalClasses) });}这将执行一个相对较轻的查询,该查询仅返回一个布尔值并且不会将新实体附加到更改跟踪器。

慕神8447489

到目前为止,我解决了以下问题:我按如下方式创建了实体类(通过使用单个外部对象而不是 ICollection 来简化示例):[Table("xyz")]public partial class TestClass{&nbsp; &nbsp; [Key]&nbsp; &nbsp; public int key {get; set;}&nbsp; &nbsp; [ForeignKey("key")]&nbsp; &nbsp; [Required]&nbsp; &nbsp; public virtual ExternalClass externalClass{get; set;}&nbsp; &nbsp; public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; ...&nbsp;&nbsp; &nbsp; }}然后在我ServiceClass创建新记录的地方:public void InsertNewRecord(TestClass newClass){&nbsp; &nbsp; newClass.externalClass = context.Where(e => e.ID = newClass.externalClass.Id).First();&nbsp; &nbsp; context.Add(newClass);&nbsp; &nbsp; context.SaveChanges();&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;}如果无法找到 externalClass(外部),newClass.externalClass则将其设置为 null 并由于[Required]注解而引发验证错误。这有一个额外的好处,即 EF 不会尝试保存外来对象,externalClass因为它现在识别出该条目仍然存在。
打开App,查看更多内容
随时随地看视频慕课网APP