5

I have a custom ValidationAttribute, it checks if another user exists already. To so it needs access to my data access layer, an instance injected into my controller by Unity

How can I pass this (or anything for that matter) as a parameter into my custom validator?

Is this possible? i.e where I'm creating Dal, that should be a paramter

public class EmailIsUnique : ValidationAttribute
    {
        private string _errorMessage = "An account with this {0} already exists";

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            DataAccessHelper Dal = new DataAccessHelper(SharedResolver.AppSettingsHelper().DbConnectionString); //todo, this is way too slow
            bool isValid = true;
            if(value == null) {
                isValid = false;
                _errorMessage = "{0} Cannot be empty";
            } else {
                string email = value.ToString();
                if (Dal.User.FindByEmail(email) != null)
                {
                    isValid = false;
                }
            }

            if (isValid)
                return ValidationResult.Success;
            else
                return new ValidationResult(String.Format(_errorMessage, validationContext.DisplayName));
        }
    }
williamsandonz
  • 15,864
  • 23
  • 100
  • 186

3 Answers3

5

I'm not too sure you'll be able to pass runtime parameters into your attribute.

You could use DependencyResolver.Current.GetService<DataAccessHelper>() to resolve your dal (given that you've registered DataAccessHelper)

You're probably more likely to have registered DataAccessHelper as IDataAccessHelper or something? in which case you'd call GetService<IDataAccessHelper>

public class EmailIsUnique : ValidationAttribute
{
    private string _errorMessage = "An account with this {0} already exists";

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        IDataAccessHelper Dal = DependencyResolver.Current.GetService<IDataAccessHelper>(); // too slow
        bool isValid = true;
        if(value == null) {
            isValid = false;
            _errorMessage = "{0} Cannot be empty";
        } else {
            string email = value.ToString();
            if (Dal.User.FindByEmail(email) != null)
            {
                isValid = false;
            }
        }

        if (isValid)
            return ValidationResult.Success;
        else
            return new ValidationResult(String.Format(_errorMessage, validationContext.DisplayName));
    }
}

or

public class EmailIsUnique : ValidationAttribute
{
    [Dependency]
    public IDataAccessHelper DataAccess {get;set;}

    private string _errorMessage = "An account with this {0} already exists";

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {

        bool isValid = true;
        if(value == null) 
        {
            isValid = false;
            _errorMessage = "{0} Cannot be empty";
        } 
       else 
       {
            string email = value.ToString();
            if (DataAccess.User.FindByEmail(email) != null)
            {
                isValid = false;
            }
        }

        if (isValid)
            return ValidationResult.Success;
        else
            return new ValidationResult(String.Format(_errorMessage, validationContext.DisplayName));
    }
}
Vaibhav Parmar
  • 645
  • 4
  • 11
Jamez
  • 1,559
  • 1
  • 15
  • 26
  • Sorry I guess my code is a bit confusing. I need to get rid of the service locator pattern here, because it's in a separated project, which should only receive dependencies. Looks like I might have to move this from an attribute to some custom code. Thanks James, appreciate it! – williamsandonz Dec 24 '13 at 09:25
  • 1
    If that's the case, you could consider abstracting your DAL with an interface, registering that interface with your IoC container and using property injection by specifying [Dependency] above the IDataAccesHelper property on your attribute? – Jamez Dec 24 '13 at 09:36
  • Yes! I just concocted the very same strategy in my head, whilst taking a walk outside, (the first part anyway!). Re the 2nd part that's awesome I didn't know that about Unity, thank you! – williamsandonz Dec 24 '13 at 09:46
  • I'm not 100% sure on the constructor, you could try it, but if it doesn't work, try the second way. If that doesn't work, the first way will definitely work. – Jamez Dec 24 '13 at 09:48
  • Sorry yes I meant the attribute, a bit drunk haha :-) – williamsandonz Dec 24 '13 at 09:57
1

You are probably looking for Property Injection. Have a look at this post. First solution is to create your custom filter provider that supports dependency injection.

Second solution is to use Service Locator pattern and get instance of you service in attribute constructor.

Community
  • 1
  • 1
Yevgeniy.Chernobrivets
  • 3,194
  • 2
  • 12
  • 14
  • Thank you. I'm using Service Locator pattern, unfortunately though I just moved my models to a DataAccess project. (for separation of concerns), and that project shouldn't have it's own Serivce locator. Looks like your link might help alot, thanks! – williamsandonz Dec 24 '13 at 09:11
0

yes.you can pass parameter which use in the Model class as below:

[EmailIsUnique()]
public string Email {get; set;}

so this will automatically pass Email as a parameter and check the value.

Vaibhav Parmar
  • 645
  • 4
  • 11