Database injection into a validation attribute with ASP MVC and Castle Windsor

风流意气都作罢 提交于 2019-11-27 15:37:27
Mauricio Scheffer

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...
Sunday Ironfoot

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).

Ruben Bartelink

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));
penderi

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!