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

后端 未结 4 546
生来不讨喜
生来不讨喜 2020-12-03 23:04

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 wor

相关标签:
4条回答
  • 2020-12-03 23:08

    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

    0 讨论(0)
  • 2020-12-03 23:12

    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));
    
    0 讨论(0)
  • 2020-12-03 23:20

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

    0 讨论(0)
  • 2020-12-03 23:30

    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...
    0 讨论(0)
提交回复
热议问题