Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Model Validation against database

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.

like image 780
Miguel Veloso Avatar asked Oct 20 '22 22:10

Miguel Veloso


2 Answers

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.

like image 99
Colin Avatar answered Oct 24 '22 11:10

Colin


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.

like image 33
Ventsyslav Raikov Avatar answered Oct 24 '22 10:10

Ventsyslav Raikov