I'm trying to get EF 4.1 working with Repository, UnitOfWork, separation of entities from EF and validation.
I followed this guide to get a nice separation of my POCO entities from the EF model and I'm now following this guide to implement validation (with IValidatableObject).
My solution consists of:
But I'm hitting a brick wall with the validation:
Anyone got any pointers for me here? I've posted 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;
}
}
Technically you could introduce an interface with an explicit implementation like so:
In Contacts.Entities assembly:
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 Contacts.Repository assembly (references Contacts.Entities assembly):
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);
}
}
Contacts.Validation assembly can be removed.
However, I do not really like this solution. Your POCO has - through the Validate
method - still a dependency on the repository, if interface or not. For a stronger separation of concerns I would probably prefer to have a separate Validation class which does perhaps also operations on the repo. Or if I would implement IValidatableObject
I would probably only do validations which depend on model object properties alone (things like "production date must not be later than shipping date" and so on). Well, it's partially a matter of taste. The second example you have linked does not really care about separation of concerns, so you have somehow a conflict with the first example.
The validation that a particular field must be unique is from my viewpoint not a validation on entity level. It could also be considered as a validation of the repo (the repo would become invalid if I'd insert an entity with same name).
Usually I access my repos through service classes and there I do a "check" before I insert if there is already an entity with same name. Not a cleanly separated validation. It might become easier and cleaner when EF provides the "Unique Constraints" feature mentioned in the second blog post.
~ Slauma Jun 28 '11 at 17:14
this comment deserved to be an answer
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With