Entity Framework IValidatableObject Link DbContext

I am trying to get EF 4.1 to work with the repository, UnitOfWork, split entities from EF and validate.

I followed this guide to get a good separation of my POCO objects from the EF model, and now I follow this guide to implement validation (using the IValidatableObject method).

My solution consists of:

  • Contacts.Repository [EF and Contacts.Entities links]:
    • Contacts.edmx
    • ContactsDbContext.cs
  • Contacts.Entities [no links]:
    • Contact.cs (Contacts.Entities.Contact partial class)
  • Contacts. Validation [Contacts.Entities and Contacts.Repository links]
    • Contact.cs (Contacts.Entities.Contact partial class)

But I hit a brick wall with a check:

  • I cannot add validation logic to Contacts.Entities because it will cause a circular link from Contacts.Repository (contact.Validate (...) you must use ContactsDbContext). Therefore, I created a separate Contact.Validation project.
  • But this means splitting the Contact class with partial classes to define a contact inside both contacts. Contacts and contacts. The code no longer compiles because you cannot define a partial class for different assemblies.

Does anyone have any pointers for me here? I sent the code below ...

Contacts.Repository.ContactsDbContext.cs:

namespace Contacts.Repository { public partial class ContactsDbContext : DbContext { public DbSet<Contact> Contacts { get; set; } protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) { items.Add("Context", this); return base.ValidateEntity(entityEntry, items); } } } 

Contacts.Entities.Contact.cs:

 namespace Contacts.Entities { public partial class Contact { public string Name { get; set; } } } 

Contacts. Validation.Contact.cs contains:

 namespace Contacts.Entities { public partial class Contact : IValidatableObject { public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { ContactsDbContext contacts = (ContactsDbContext)validationContext.Items["Context"]; //Check if Contact already exists with the same Name if (contacts.Any<Contact>(c => c.Name == this.Name)) yield return new ValidationResult("Contact 'Name' is already in use.", new string[] { "Name" }); yield break; } } 
+6
source share
2 answers

Technically, you can enter an interface with an explicit implementation like this:

In Contacts. Custom builds:

 public interface IContactsDbContext { IQueryable<Contact> Contacts { get; } // Not DbSet<Contact> because you don't want dependency on EF assembly } //... public class Contact : IValidatableObject // No partial class anymore { public string Name { get; set; } public IEnumerable<ValidationResult> Validate( ValidationContext validationContext) { IContactsDbContext context = validationContext.Items["Context"] as IContactsDbContext; if (context.Contacts.Any<Contact>(c => c.Name == this.Name)) yield return new ValidationResult( "Contact 'Name' is already in use.", new string[] { "Name" }); yield break; } // IValidatableObject, ValidationResult and ValidationContext is in // System.ComponentModel.DataAnnotations.dll, so no dependency on EF } 

In the assembly Contacts. Repository (links: Contacts. Creating sites):

 public class ContactsDbContext : DbContext, IContactsDbContext { public DbSet<Contact> Contacts { get; set; } IQueryable<Contact> IContactsDbContext.Contacts // explicit impl. { get { return Contacts; } // works because DbSet is an IQueryable } protected override DbEntityValidationResult ValidateEntity( DbEntityEntry entityEntry, IDictionary<object, object> items) { items.Add("Context", this); return base.ValidateEntity(entityEntry, items); } } 

Console> Contacts. > can be deleted.

However, I do not really like this solution. Your POCO has - through the Validate method - still a repository dependency, if the interface or not. For a stronger separation of concerns, I would probably prefer to have a separate Validation class, which may also perform repo operations. Or, if I implemented IValidatableObject , I would probably only do checks that depend only on the properties of the model object (such as “production date should not be later than the delivery date”, etc.). Well, it's partly a matter of taste. The second example you linked does not really care about the separation of problems, so you have some kind of conflict with the first example.

+7
source

Validating that a particular field must be unique is my point of view, not a check at the entity level. It could also be considered a repo check (the repo would become invalid if I entered an object with the same name).

I usually access my repositories through service classes, and there I do a “check” before inserting if there is already an entity with the same name. Not a purely split check. It can become simpler and cleaner when EF provides the "Unique Constraints" feature mentioned in the second blog post.

~ Slauma Jun 28 at 17:14

This comment deserves an answer.

+1
source

Source: https://habr.com/ru/post/891462/


All Articles