Reputation: 1390
I'm new to Inversion of Control & Autofac. Going through the documentation, there is a lot ASP.NET Web Api concepts I do not fully understand or have experience with, making it a bit difficult to determine what is missing from my implementation.
I want to apply Autofac to an existing ASP.NET Web Api project that has several ApiControllers. These controllers all share a parent abstract class. This abstract class has a method that is responsible for returning an instance of a service. I hoped to replace this method with Autofac's dependency injection.
The parent abstract class from which every ApiController inherits, is very simple.
public abstract class BaseApiController
{
public IMyService serviceClient { get; set; }
public BaseApiController() {}
public BaseApiController(IMyService serviceClient)
{
this.serviceClient = serviceClient;
}
}
Every controller inherits from the above class, while some employs a default Get method, most have multiple routes. Not a single controller is specifying a constructor:
public class MyController : BaseApiController
{
public MyController() : base() {}
public MyController(IMyService serviceClient) : base(serviceClient) {}
[HttpGet]
[Route("api/foo/bar")]
[ActionName("FooBar")]
public string FooBar()
{
using (serviceClient)
{
return serviceClient.GetFooBar() as string;
}
}
}
Autofac is integrated into the Application_Start method of the applications Glabal.asax.cs, registering the ServerClientProvider which should be the provider that should be resolved to when a dependency to the IMyService is encountered:
public class Global : System.Web.HttpApplication, IContainerProviderAccessor
{
#region AutoFac
private static IContainerProvider _containerProvider;
public IContainerProvider ContainerProvider => _containerProvider;
#endregion AutoFac
protected void Application_Start(object sender, EventArgs e)
{
var builder = new ContainerBuilder();
builder.Register(x => ServiceClientProvider.GetServiceClient())
.As<IMyService>()
.InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.RegisterHubs(Assembly.GetExecutingAssembly()).PropertiesAutowired();
var container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);
_containerProvider = new ContainerProvider(container);
}
}
I have configured the ASP.NET application, in the web.config:
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ContainerDisposal" type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web" preCondition="managedHandler" />
<add name="PropertyInjection" type="Autofac.Integration.Web.Forms.PropertyInjectionModule, Autofac.Integration.Web" preCondition="managedHandler" />
</modules>
</system.webServer>
<configuration>
As I understand it, Autofac should automatically resolve the provider when encountering a controller with a public property that matches the registered type (Property Injection), or if a constructor exists with a parameter that matches any registered dependency.
However, I'm not getting any errors relating to my Autofac configuration. I'm getting a NullReferenceException when the MyController
attempt to call the IMyService.FooBar
method.
What am I missing?
Any help would be greatly appreciated.
Upvotes: 0
Views: 2558
Reputation: 1390
After several attempts to debug the code, I realized that there existed another start up implementation in the project. OWIN is used and the start-up implementation I was trying to integrate Autofac with is instantiated but never used. So following the instructions on https://autofac.readthedocs.io/en/latest/integration/owin.html, yielded the results I was looking for.
Here is how the start-up implemetation should look at:
[assembly: OwinStartup(typeof(MyWebApp.Api.StartUp))]
namespace MyWebApp.Api
{
public class StartUp
{
public void Configuration(IAppBuilder app)
{
// In OWIN you create your own HttpConfiguration rather than
// re-using the GlobalConfiguration.
HttpConfiguration httpconfig = new HttpConfiguration();
httpconfig.Routes.MapHttpRoute(
name: "ApiRouteWithAction",
routeTemplate: "api/{controller}/{action}");
httpconfig.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Autofac SignalR integration
var builder = new ContainerBuilder();
// Register Web API controller in executing assembly.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// Register SignalR hubs.
builder.RegisterHubs(Assembly.GetExecutingAssembly());
// Registering Deletegates
builder.Register(x => ServiceClientProvider.GetServiceClient())
.As<IMyService>()
.InstancePerRequest();
// Set the dependency resolver to be Autofac.
var container = builder.Build();
httpconfig.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseWebApi(httpconfig);
// Register the Autofac middleware FIRST, then the custom middleware if any.
app.UseAutofacMiddleware(container);
}
}
}
The controller & abstract class it inherited from, were tweaked, removing the parameterless constructors.
public abstract class BaseApiController
{
public IMyService serviceClient { get; set; }
public BaseApiController(IMyService serviceClient)
{
this.serviceClient = serviceClient;
}
}
Every controller inherits from the above class, while some employs a default Get method, most have multiple routes. Not a single controller is specifying a constructor:
public class MyController : BaseApiController
{
public MyController(IMyService serviceClient) : base(serviceClient) {}
[HttpGet]
[Route("api/foo/bar")]
[ActionName("FooBar")]
public string FooBar()
{
using (serviceClient)
{
return serviceClient.GetFooBar() as string;
}
}
}
Thanks for the interest, hopefully my rookie mistake will help another rookie.
Upvotes: 0