Entity Framework - Validation of Foreign Keys

c# entity-framework entity-framework-6

Question

being equipped withTestClass :

[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)
    {
        ... 
    }
}

How do I configure the class to fulfil the following requirements:

  • Entity Framework unit testing is what I want to use to test the model.
  • During creation, theexternalClasses ought not to be rescued (should already be in database)
  • Every external class must exist in the database, according to the EF. Throw an exception if not.

My first notion was to set the foreign objects to null and ask the database to verify for the existence of the foreign-key(s) inside the validate function. However, this method does not perform well with unit testing, and I also believe that having database queries inside a model is not very clean.

Does anybody have suggestions for a clean use of the EF to manage that?

1
1
7/31/2018 11:00:12 AM

Accepted Answer

in my opinion it's not that clean to have database requests within a model

I usually agree, but then I usually make an exception just to be sure. Why?

When IValidatableObject decides to do validation, it should be the sole point of validation (by which I mean that theValidate For performing more intricate validations involving several entities, the technique is not the ideal location to do so.

Having a portion of the "basic" validation criteria in the entity itself and a different portion, well, anywhere, would be really awkward. The issue is that it might be anyplace. Therefore, it may be omitted when storing the object through a different code route.

The context that runs the validation is often made accessible to the user as a result.validationContext This is accomplished by overriding the context'sValidateEntity method referred to bySaveChanges (when context.Configuration.ValidateOnSaveEnabled is true ):

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
    items.Add("context", this);
    return base.ValidateEntity(entityEntry, items);
}

At this point, it is feasible to understand the context inside theValidate method:

var context = (MyContext)validationContext.Items["context"];

Additionally, it has the ability to do database queries.

This should be handled with care though. It's wise to follow these three guidelines:

  1. Run projection-only searches rather than those that alter the change tracker's data, or complete entities. While certain monitored entities are waiting to be saved to the database, validation is being done. It is best not to make any changes to this group of monitored entities.
  2. Run light queries instead.
  3. When the programme consistently saves substantial quantities of the entity in a single unit of work, avoid doing this. (If applicable, useIValidatableObject is not the wisest course of action).

With this in place it's possible to run a validation like:

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

This runs a really simple query that doesn't add any new entities to the change tracker and merely returns a boolean answer.

1
7/31/2018 2:09:22 PM

Popular Answer

Up to now, I came up with the following solution:

The entity class was constructed as follows: (made the example more straightforward by using a single foreign object rather than an 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)
    {
        ... 
    }
}

next in myServiceClass in which I'm setting a new record:

public void InsertNewRecord(TestClass newClass){
    newClass.externalClass = context.Where(e => e.ID = newClass.externalClass.Id).First();
    context.Add(newClass);
    context.SaveChanges();        
}

If the externalClass (foreign) could not be found,newClass.externalClass is set to null, and a validation error is then generated as a result of the[Required] Annotation. The added benefit of this is that EF doesn't attempt to save the foreign object.externalClass since it is now aware that this entry is still present.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow