7

I need some help - I am trying to use a custom validation attribute in an ASP.NET MVC web project that needs to make a database call.

I have windsor successfully working for the controllers and the IRepository interface is injected normally. The problem arrises when I need to inject the repository into the attribute class.

The attribute class has the following code:

public class ValidateUniqueUrlNodeAttribute : AbstractValidationAttribute
{
    private readonly string message;
    private readonly IArticleRepository articleRepository;

    public ValidateUniqueUrlNodeAttribute(string message)
    {
        this.message = message;
    }

    public ValidateUniqueUrlNodeAttribute(string message, IArticleRepository articleRepository):this(message)
    {
        this.articleRepository = articleRepository;
    }
    public override IValidator Build()
    {
        var validator = new UniqueUrlNodeValidator(articleRepository) { ErrorMessage = message };

        ConfigureValidatorMessage(validator);

        return validator;
    }

My problem is that I cannot seem to make Windsor intercept the contruction of the attribute to pass in the IArticleRepository

The current code in my global.asax file is as follows:

container = new WindsorContainer();
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(Container));
   container
     .RegisterControllers(Assembly.GetExecutingAssembly())
     .AddComponent<IArticleRepository, ArticleRepository>()
     .AddComponent<ValidateUniqueUrlNodeAttribute>();

Any help would be greatly appreciated.

0

4 Answers 4

5

AFAIK no dependency injection container can directly manage an attribute, since it's instantiated by the runtime and there's no way to intercept that.

However, they can cheat by either:

  1. Using a static gateway to the container (example), or
  2. Using a "BuildUp" feature that injects whatever dependencies are found within an already-constructed object. This is called BuildUp in Unity or InjectProperties in Autofac.

Windsor doesn't support #2 (ref1, ref2), so you can either:

  1. Try one of the hacks to make Windsor support #2 (hack1, hack2)
  2. Use a static gateway
  3. Implement your own IValidatorBuilder and make it use Windsor to create validators. I'm sure this is implemented somewhere but I can't find it right now...
Sign up to request clarification or add additional context in comments.

Comments

0

Don't know if this helps, but I subclassed ValidationAttribute to expose a Resolve<T>() method like so:

public abstract class IocValidationAttribute : ValidationAttribute
{
    protected T Resolve<T>()
    {
        return IocHelper.Container().Resolve<T>();
    }
}

Then it can be used in any custom ValidatorAttribute that needs to hit a database:

public class UniqueEmailAttribute : IocValidationAttribute
{
    public override bool IsValid(object value)
    {
        ICustomerRepository customerRepository = Resolve<ICustomerRepository>();

        return customerRepository.FindByEmail(value.ToString()) == null;
    }
}

I think it's a variation of the 'Static Gateway' approach mentioned by Mauricio Scheffer. I don't know if this is a good design or not. I'm not a huge fan of it, I'd rather the dependency was injected more 'elegantly', though I can't use constructor injection obviously, I'd like to use Property injection but can't work out a way to hook into the ASP.NET MVC framework code to do this (I've even pored though the MVC2 source code).

2 Comments

Where do you get IocHelper from?
@soniiic looks like an evil static
0

I was able to wire it up [using Autofac as it happens, but it's just constructor injection via the ASP.NET MVC DependencyResolver] in this answer, enabling one to write:

class MyModel 
{
    ...
    [Required, StringLength(42)]
    [ValidatorService(typeof(MyDiDependentValidator), ErrorMessage = "It's simply unacceptable")]
    public string MyProperty { get; set; }
    ....
}

public class MyDiDependentValidator : Validator<MyModel>
{
    readonly IUnitOfWork _iLoveWrappingStuff;

    public MyDiDependentValidator(IUnitOfWork iLoveWrappingStuff)
    {
        _iLoveWrappingStuff = iLoveWrappingStuff;
    }

    protected override bool IsValid(MyModel instance, object value)
    {
        var attempted = (string)value;
        return _iLoveWrappingStuff.SaysCanHazCheez(instance, attempted);
    }
}

With some helper classes (look over there), you wire it up e.g. in ASP.NET MVC like so in the Global.asax :-

DataAnnotationsModelValidatorProvider.RegisterAdapterFactory(
    typeof(ValidatorServiceAttribute),
    (metadata, context, attribute) =>
        new DataAnnotationsModelValidatorEx(metadata, context, attribute, true));

Comments

-1

Hmm.

Can you test the effect of removing the (string message) ctor, and see if that at least forces Castle to use the ctor with the Repostiory ?

Otherwise we call AddComponent(name, type, type). Other than that it really should work...

Also does this hint at my first idea ? How do I use Windsor to inject dependencies into ActionFilterAttributes

2 Comments

Sorry, I can't remove the string CTOR as the attribute looks like this: [ValidateUniqueUrlNode("Please enter a unique Url")] public string UrlNode { get; set; } Should this be different?
Dahhh. Still scratching head... I'll have a search

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.