views:

683

answers:

3

Hi,

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.

A: 

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 ? http://stackoverflow.com/questions/553330/how-do-i-use-windsor-to-inject-dependencies-into-actionfilterattributes

ip
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?
David E
Dahhh. Still scratching head... I'll have a search
ip
+1  A: 

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...
Mauricio Scheffer
Many thanks. I opted #1 which meets my needs perfectly
David E
A: 

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 poured though the MVC2 source code).

Sunday Ironfoot