5

I want to use EF 5 model validation to avoid duplicate values in the database, so I'm using a model class like this:

[Table("MeasureUnits")]
public class MeasureUnit : IValidatableObject
{
    public int MeasureUnitId { get; set; }

    public string Symbol { get; set; }

    public string Name { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        using (MeasureUnitRepository rep = new MeasureUnitRepository())
        {
            MeasureUnit measureUnit = rep.FindDuplicateBySymbol(this);

            if (measureUnit != null)
                yield return new ValidationResult(
                    "There is another unit with this symbol, you can't duplicate it", 
                    new[] { "Symbol" });
        }
    }

The repository class creates the DbContext, implements IDisposable, has the logic to find the duplicate and it all works just as intended.

However, using the debugger I realized the validation is performed twice for every insert or update, so the repository (and DbContext) gets instantiated and disposed twice also.

Besides that, there is another DbContext living in the controller but just don't find the way to use it inside the model class, other than including the DbContext in the model's constructor, but I feel it's not the right solution.

Is there a better o "right" way to achieve this validation?

Thanks in advance.

Miguel Veloso
  • 1,055
  • 10
  • 16
  • 1
    Depending on how you want to handle validation errors creating a unique index in the database might be easier and will perform better (however in case of duplicates you will get an UpdateException instead of validation exception) – Pawel Oct 16 '13 at 14:28
  • @Pawel I would use an index to speed up the FindDuplicateBy... but I just prefer to catch it before the changes get posted. However, now that you mention it, I realize I have to handle that too, because it is possible that duplication occurs after validation but before the actual commit. (+1 for that) – Miguel Veloso Oct 16 '13 at 14:58

2 Answers2

2

When you have to go to the database then you need to use DbContext and DbContext has an Overridable method called ValidateEntity. See this article: Entity Framework Validation.

I put the code I use in another answer here

And more about how I've structured the validation in MVC here.

Also, instantiating a context inside your repository is likely to cause you grief. The repositories will need to share a context. You could treat the context as your unit of work and pass it into the repository in the constructor, or you could wrap the context in your own unit of work and pass that in.

Community
  • 1
  • 1
Colin
  • 22,328
  • 17
  • 103
  • 197
  • I don't like coding the duplication logic for all models in the DbContext (overriding ValidateEntity), however it looks like I could use the "items" parameter of ValidateEntity to get "something" like (DbContext, the Repository or my UnitOfWork) into the model, (through ValidationContext), so instead of instantiating DbContext or Repository I would just use it within each model, so the logic would be where it belongs (IMHO). Does it look right? – Miguel Veloso Oct 16 '13 at 15:45
  • 1
    The solution at the link I put in my comment on the other question does that, but I think that having POCO's dependent on DbContexts or Repositories or Units Of Work is worse than validating in the context. At the end of the day validation is a cross cutting concern. Sometimes it's on the client, sometimes it's on the web server, sometimes it's in the database. Often it's in all three – Colin Oct 16 '13 at 15:54
  • @Bond I regret having to check just one answer, because it's the combination of both, but that's just the way it is, Thank you both. – Miguel Veloso Oct 16 '13 at 16:43
1

You can use any IOC container available out there like Unity, Ninject, Autofac or StructureMap to inject your repository as a dependency.

This way you would be able to access the same object in the controller, your Validate method or wherever you need to use it.

Some of these IOC(Ninject for sure - look for 'request scope') containers are capable of integrating with ASP.NET MVC so that the dependency(your repository in that case) is created once per request and disposed when the request ends.

Example using Ninject:

You create a globally accessible(the design is up to you) ninject kernel

public static class NinjectKernel
{
    public static IKernel Kernel = new StandardKernel();
    static NinjectKernel()
    {
        Kernel.Bind<IMyRepository>().To<MyRepositoryImpl>().InRequestScope();
    }
}

and a controller factory for MVC controllers

public class NinjectControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext,
    Type controllerType)
    {
        return controllerType == null ? null : (IController)NinjectKernel.Kernel.Get(controllerType);
    }
}

You can then set your controller factory in Global.asax like this

ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());

and get the repository in your Validate method in a similar way it's done in the Controller factory.

Ventsyslav Raikov
  • 6,882
  • 1
  • 25
  • 28
  • I haven't really done anything important with IOC, but I understand I would have to include the repository in the model class constructor, and then IOC would take care, is that so? however I just don't get it completely, would you mind giving some example? – Miguel Veloso Oct 16 '13 at 15:06
  • 1
    yes - exactly, the IOC will take care to inject a new object into your contorller's constructor and return that same object every time you need it during the lifetime of the request. Example would depend on the IOC container you choose to use, I'll update my answer with a Ninject example in few minutes – Ventsyslav Raikov Oct 16 '13 at 15:23
  • The point, if I'm right, is that I need the repository injected in the MODEL class, not the CONTROLLER class, is it so or am I completely off target? – Miguel Veloso Oct 16 '13 at 15:34
  • 1
    You would be able to access it in your MODEL class too but you don't usually create your models with the IOC container(although that's possible too). – Ventsyslav Raikov Oct 16 '13 at 15:40
  • @MiguelVeloso while it's possible to inject the repositories into the POCO's, it is not desirable. More discussion here: http://stackoverflow.com/a/6497228/150342 – Colin Oct 16 '13 at 15:46
  • 1
    @MiguelVeloso - exactly. It's not a good design to do that and you would end up with a messy code base, considering all the frameworks(like EF, and MVC's model binder) that need to create models on the go. – Ventsyslav Raikov Oct 16 '13 at 15:54
  • @Colin, Hummm, I think that it all gets down to define whose responsibility it is to avoid duplication... and now that you both question my approach, I am rethinking it looks like it's the repository's or the DbContext's, but not the model's, because the model doesn't know of others like it, it's the repository/DbContext that does! Well... that looks like a sound reason to get the repo/DbContext out of the POCO, so I've just switched my mind! Thank you guys! – Miguel Veloso Oct 16 '13 at 16:17