Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实体框架-外键验证_C#_Entity Framework_Entity Framework 6 - Fatal编程技术网

C# 实体框架-外键验证

C# 实体框架-外键验证,c#,entity-framework,entity-framework-6,C#,Entity Framework,Entity Framework 6,具有以下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&

具有以下
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)
    {
        ... 
    }
}
[表(“xyz”)]
公共部分类TestClass{
[关键]
公共int密钥{get;set;}
[外国钥匙(“钥匙”)]
公共虚拟ICollection外部类{get;set;}
公共IEnumerable验证(ValidationContext ValidationContext)
{
... 
}
}
我需要如何设置该类以满足以下要求:

  • 我想用实体框架单元测试来测试模型
  • 在生产环境中,
    externalClasses
    不应该保存(应该已经在数据库中)
  • EF必须确保每个外部类都存在于DB中。如果不是,抛出异常
我的第一个想法是将外部对象设置为null,并在validate方法中请求数据库检查外键是否存在。但是这种方法在单元测试中效果不太好,而且在我看来,在一个模型中有数据库请求也不是那么干净

有人知道如何使用EF以干净的方式处理吗

在我看来,在一个模型中有数据库请求是不干净的

我倾向于同意,但为了验证,我倾向于破例。为什么?

当决定由其进行验证时,它应尽可能成为唯一的验证点(我的意思是,
Validate
方法不是应用涉及其他实体的更复杂验证的最佳场所)

将“简单”验证规则的一部分放在实体本身中,另一部分放在任何地方都会非常不方便。这就是问题所在:它可以在任何地方。因此,当通过另一个代码路径保存实体时,可以跳过它

因此,我倾向于将执行验证的上下文提供给
validationContext
。这是通过重写上下文的
ValidateEntity
方法完成的,该方法由
SaveChanges
调用(当
context.Configuration.ValidateOnSaveEnabled
true
时):

…并可用于执行数据库查询

不过,这应该小心处理。遵守三条规则很好:

  • 不要运行更改更改跟踪程序内容的查询,也就是说,不要查询完整的实体,只查询投影。验证是在许多被跟踪的实体等待保存到数据库时执行的。最好不要更改此跟踪实体集合中的任何内容
  • 不要运行繁重的查询
  • 当应用程序在一个工作单元中定期保存大量实体时,不要这样做。(在这种情况下,使用
    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) });
    }
    

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

    直到现在,我解决了以下问题:

    我创建了entity类,如下所示(通过使用单个外部对象而不是ICollection简化了示例):


    如果找不到externalClass(外部),则会将
    newClass.externalClass
    设置为null,并由于
    [必需]
    注释而引发验证错误。这还有一个额外的优点,EF不会试图保存外部对象
    externalClass
    ,因为它现在识别出这个条目仍然存在。

    我们可以通过在单元测试中模拟这些功能来禁止保存在externalClass上的数据。例如:你可以使用MoQ,RhinoMock@SULFIKAR啊,对不起。。我忘了提到我确实使用最小起订量。但我不确定在模型验证方法中包含数据库请求是否干净。没有其他解决方案吗?此外,我总是需要手动从外部对象填充外键,这是一个难题。如果有任何解决方案,EF会自动将对象映射到外键,那就太好了。哇,非常感谢您的精彩和广泛的解释。这对我理解EF的用法和模式有很大帮助。非常感谢。
    var context = (MyContext)validationContext.Items["context"];
    
    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) });
    }
    
    [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)
        {
            ... 
        }
    }
    
    public void InsertNewRecord(TestClass newClass){
        newClass.externalClass = context.Where(e => e.ID = newClass.externalClass.Id).First();
        context.Add(newClass);
        context.SaveChanges();        
    }