为了账号安全,请及时绑定邮箱和手机立即绑定

实体框架 - 外键验证

实体框架 - 外键验证

C#
MM们 2021-11-07 20:19:22
具有以下内容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 以干净的方式处理这个问题?
查看完整描述

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

}

这将执行一个相对较轻的查询,该查询仅返回一个布尔值并且不会将新实体附加到更改跟踪器。


查看完整回答
反对 回复 2021-11-07
?
慕神8447489

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因为它现在识别出该条目仍然存在。


查看完整回答
反对 回复 2021-11-07
  • 2 回答
  • 0 关注
  • 180 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信