2

An ASP.NET Core 2.1 MVC app, using Autofac following their documentation on setup (https://autofaccn.readthedocs.io/en/latest/integration/aspnetcore.html).

I am trying to resolve a dependency in a custom ValidationAttribute. The returned value from valicationContext.GetService is always returning null. Inspecting the validatationContext the private member serviceProvider is always null.

what am I missing in the setup that this isnt working. The dependancies resolve everywhere else in the app, just not in the ValidationAttributes.

public class MyCustomAttribute : ValidationAttribute
{

    public MyCustomAttribute ()
    {

    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // THIS IS ALWAYS RETURNING NULL
        var IMyService service = (IMyService)validationContext.GetService(typeof(IMyService));

        return ValidationResult.Success;
    }
}
dhaas
  • 21
  • 1
  • 2

1 Answers1

1

I can't say I've tried this before, but doing some searching I found this issue which seems to indicate that the service provider (not the ServiceContainer property, but the service provider that will respond to GetService calls) should always be populated. Granted, that's from an archived repo from early on in .NET Core, but it should still hold.

Looking at the source for ValidationContext I see that the private serviceProvider field is actually a function that needs to be instantiated somewhere; it's not actually a reference to a provider proper. That means if it's null, one of two things is happening:

  1. The path through ASP.NET Core that's instantiating that ValidationContext is not passing in the IServiceProvider required to provide services.
  2. Something is broken.

If it's #1, I'd guess there are a variety of potential reasons. I'd think about things like...

  • The attribute is being used by something running outside the "ASP.NET Pipeline" - like a manually invoked validation or possibly something at application startup where there's no request at the moment.
  • The attribute is being used in a test where the full pipeline isn't in effect.

Something like that. I'm not saying this is what's happening, but I've seen questions like this before where it appears something isn't working right when it's actually an application code problem. For example, there are lots of questions about why "instance-per-request" dependencies aren't working and it turns out the code is running on a background thread where there's no request so... yeah. I don't know how your app works, but that sort of "I'm doing something I forgot to mention because I didn't think it was relevant" stuff comes into play here.

Let's assume you've got a super vanilla ASP.NET Core app, though. Based on the issue I mentioned earlier and the code you've posted, this looks like it should work but it's not. There shouldn't be anything you need to wire up for this, it should just work. Given that, you might want to file an issue about it. You may have found something that legitimately isn't working.

Before you do that, you might want to debug a little more. You can step right into ASP.NET Core source code and that could help you figure out what's up. The article I linked there explains how to set it up. It's not a two step process and needs screen shots to help or I'd put it right in here.

Set a breakpoint on your failing statement up there and then switch over to the Visual Studio "call stack" window. Click on the call stack frames higher up in the stack and see what's actually creating that context object. With a little clicking around and intuition you can probably figure out where the issue is. Maybe it'll point to something in your app you didn't realize you were doing, maybe it'll point to a bug in ASP.NET Core. If it's a bug in ASP.NET Core, having the information you found from your debugging session will be really helpful to that team.

Finally, I'd be remiss if I didn't mention that manually resolving a service in an attribute like this is technically service location rather than dependency injection and it'd be a better all-around solution if you avoided it entirely. There's an extremely similar question to yours right here walking through how to get around this with Simple Injector, though the principle holds for Autofac, too. Setting up model validators and using a model validator provider rather than attributes might be a better, more testable way to go. The answer in that question has more explanation on this.

Travis Illig
  • 23,195
  • 2
  • 62
  • 85