3

My business layer has objects that implement IValidatableObject. I want my business rules for these entities in an external assembly and then call the validator for the type in:

public IEnumerable<ValidationResult> Validate(ValidationContext vc){}

I was going to use DI to inject the validator into the constructor of the type implementing the IValidatableObject interface.

   public Customer(ICustomerValidator validator)
        {
            this.Validator = validator;
        }

I figured I'd call out to Unity in the types factory and inject from there.

But then alot of the time the entity is just read not modified i.e. a Customer entity. It doesn't always need a validator because it might just be there to support a use case and not actually be modified by it.

So I figure I can either:

  1. Make CustomerEdit classes and then use constructor injector on those only and not on say CustomerRead classes but this leads to a class explosion that I dont really want for this application.
  2. Have a single customer class and inject the validator when it's needed.

For 2 as above the Validate method of the implementation for IValidatableObject interface seems the appropriate place. The trouble is in many cases this method is called by code other than my own. For instance from Entity Framework and for some types, from MVC as they will be used as model classes.

So now I'm in the position of having to override methods in framework code just so I can DI. This doesn't "seem" right but to be honest I don't know why.

The next thing I looked at is the GetService() method of the ValidationContext.

public IEnumerable<ValidationResult> Validate(ValidationContext vc)
{

var customerValidator = (ICustomerValidator)vc.GetService(typeof(ICustomerValidator));
return customerValidator.Validate();

}

But if I start doing this, haven't I just stepped into the whole realm of the ServiceLocator anti pattern, breaking the Law of Demeter and making test a total pain?

So is there a cleaner way out of this or is it just a matter of choosing my trade offs from what I've outlined?

saggu
  • 73
  • 5
rism
  • 11,932
  • 16
  • 76
  • 116
  • 2
    I'm not sure it's a good idea to make your domain entities part of the dependency injection context. – L-Four Mar 27 '13 at 08:58
  • Related: http://stackoverflow.com/questions/4835046/why-not-use-an-ioc-container-to-resolve-dependencies-for-entities-business-objec – Steven Mar 27 '13 at 09:35

2 Answers2

2

Most important principle : KISS.

So unless you're creating 10000s of your business objects, don't bother, and inject your validation service all the time.

If you have a performance penalty for doing so, split the classes into read/edit model if needed.

mathieu
  • 30,974
  • 4
  • 64
  • 90
  • Overengineering you think? Yep you're probably right. I did consider this but then I figured I'd ask around, since there's KISS and then there's ignorance.... and some of this stuff is quite new too me. ;) – rism Mar 27 '13 at 08:38
  • As long as you have clean separation of concerns, and unit testing, refactoring should not be painful. – mathieu Mar 27 '13 at 08:39
1

Stevens comments on the question led me to a couple of blog posts that are related to my question.

This gist of these blog posts is that constructor injection is overused when the dependency being injected is only "used some of the time" and yet by including the dependency in the constructor we make it "required all of the time".

In the case of this article the described dependency is an OrderShipper but it could just as easily be a Validator as per my question.

So it starts with

Constructor over-injection anti-pattern - Jeffrey Palermo

which is rebutted here

Rebuttal: Constructor over-injection anti-pattern by Mark Seemann

and then the rebuttal is further refined here:

Enabling DI for Lazy Components by Mark Seemann

So accepted answer aside, if I really wanted to push my design re: injecting validators into the domain class then I could instead inject an AbstractFactory.

As always, when the dependency's lifetime may be shorter than the consumer, the solution is to inject (via the constructor!) an Abstract Factory - Mark Seemann

rism
  • 11,932
  • 16
  • 76
  • 116