2

I need to get a registered instance of type IUserService in my authentication handler.

// Register services

// Build the container.
 var containr = builder.Build();
 var resolver = new AutofacWebApiDependencyResolver(container);
 configuration.DependencyResolver = resolver;

When I run now this line of code:

var userService = configuration.DependencyResolver.GetService(typeof (IUserService)) as IUserService;

I get this exception:

   An exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll but was not handled in user code

    Additional information: No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.

I do NOT want to create the userservice manually because then I have to resolve also manually the depending classes...

How do I get a certain service when I am not inside a request? Dependency.Resolver is unknown/can not be resolved to use that somehow.

UPDATE

I changed my service registration now to:

  builder.RegisterType<UserService>()
                   .As<IUserService>()
                   .WithParameter(namedParameter)
                   .InstancePerLifetimeScope();

instead of InstancePerRequestApi()

and resolve the user service like that:

var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;

            var bla = resolver.GetRootLifetimeScope();
            IUserService userService = bla.Resolve<IUserService>(); // Woot!

That works, but what about my former InstancePerApiRequest ? I would like to keep it too!

UPDATE

public static void Register(HttpSelfHostConfiguration config)
        {
            config.MapHttpAttributeRoutes();

            var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;

            var scope = resolver.GetRootLifetimeScope();
            var userService = scope.Resolve<IUserService>();
            scope.Dispose();

            var authenticationHandler = new AuthenticationHandler(userService);
            var tokenHandler = new TokenHandler();

            config.MessageHandlers.Add(new HttpsHandler());
            config.MessageHandlers.Add(new AllowCommonVerbsHandler());
            config.MessageHandlers.Add(authenticationHandler);
            config.MessageHandlers.Add(tokenHandler);

            config.Routes.MapHttpRoute(
                name: "Authentication",
                routeTemplate: "api/users/{id}",
                defaults: new { controller = "users", id = RouteParameter.Optional },
                constraints: null,
                handler: authenticationHandler // Put this handler later on the DefaultApi
            );
            config.Routes.MapHttpRoute(
               name: "TokenApi",
               routeTemplate: "api/{controller}/{id}",
               defaults: new { id = RouteParameter.Optional },
               constraints: null,
               handler: tokenHandler
               );
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { controller = "Home", action = "Start" }
                );

            var jsonFormatter = new JsonMediaTypeFormatter();
            config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
        }
Elisabeth
  • 20,496
  • 52
  • 200
  • 321

2 Answers2

1

I assume that your authentication handler is a DelegatingHandler. If this is the case, you can access the lifetime scope for the current request using the following code:

var scope = request.GetDependencyScope();
var requestScope = scope.GetRequestLifetimeScope();

In the code above request is the HttpRequestMessage instance pass to the SendAsync method of the DelegatingHandler.

You will not be able to have the constructor dependency in this case. Inside the SendAsync method use the code I provided above to resolve the IUserService instance when you need to use it.

To create a test for your handler you will need to mock the Web API dependency scope.

var builder = new ContainerBuilder();
// Configure container for test.
var container = builder.Build();
var request = new HttpRequestMessage();
var lifetimeScope = container.BeginLifetimeScope(AutofacWebApiDependencyResolver.ApiRequestTag);
var scope = new AutofacWebApiDependencyScope(lifetimeScope);
request.Properties.Add(HttpPropertyKeys.DependencyScope, scope);

See the HttpMessageInvoker for how to unit test a DelegatingHandler.

http://nerditorium.danielauger.com/blog/2013/02/05/unit-testing-a-webapi-delegatinghandler-with-a-dependencyscope-via-an-httpmessageinvoker/

Alex Meyer-Gleaves
  • 3,821
  • 21
  • 11
  • Hello Alex, I am not inside a delegate handler. I am inside the static routes.Register method where I create an authenticationhandler which needs the IUserService in the constructor. See my update. – Elisabeth Jan 29 '14 at 20:48
  • Ok now its crashing for everry solution: 1) I have registered the IUserService as InstancePerApiRequest then its crashing in the WebApiRoutesConfig with these lines: var scope = resolver.GetRootLifetimeScope(); var userService = scope.Resolve(); OR 2) I have registered the IUserService as InstancePerLifetimeScope then its crashing in the integration test when the controller is about to be created: "Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed." – Elisabeth Jan 30 '14 at 14:27
  • I have updated my answer to note that constructor injection is not possible in this scenario. This is a limitation imposed by Web API itself. – Alex Meyer-Gleaves Feb 01 '14 at 12:37
  • I have refactored my business logic and it seems now I do not need the userService instance in the webApiConfig file :-) – Elisabeth Feb 05 '14 at 10:02
  • Just in case someone runs into the same issue... these namespace need to be included: using Autofac; using Autofac.Integration.WebApi; – MeTitus Feb 19 '16 at 15:52
0

I think you may have forgotten to add readonly while creating an object of class while using IRepository or you may have forgotten the access modifier before declaring constructor. Hope this might help you.

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
Suraj Bhatia
  • 97
  • 1
  • 4