Reputation: 1306
I have a single ASP.NET 5.0 (vnext) project where I am implementing both a Web Api and an Mvc front end. I want my Mvc controller to call the Web Api controller, which is working just fine. I built the api based on the example at http://www.asp.net/vnext/overview/aspnet-vnext/create-a-web-api-with-mvc-6, and it is working great. The Mvc front end can call the WebApi controller successfully, but the ITodoRepository doesn't get provided by the dependency injection framework when I instantiate it from the Mvc controller.
public class Startup
{
public void Configure(IApplicationBuilder app, ILoggerFactory logFactory)
{
...
app.UseServices(services =>
{
services.AddSingleton<ITodoRepository, TodoRepository>();
});
...
[Route("api/[controller]")]
public class TodoController : Controller
{
/* The ITodoRepository gets created and injected, but only when the class is activated by Mvc */
TodoController(ITodoRepository repository)
{
_repository = repository;
}
[HttpGet]
public IEnumerable<TodoItem> Get()
{
return _repository.AllItems;
}
...
public class HomeController : Controller
{
public IActionResult Index()
{
var tc = new TodoController(/* have to create my own ITodoRepository here */);
return View(tc.Get());
}
...
I was able to add an ITodoRepository to the HomeController with the [Activate] attribute, and then pass that to the constructor for the TodoController, but that doesn't pass the smell test to me. Home Controller shouldn't have to have or even know about those.
Is there another way to create the TodoController instance that will invoke the DI logic and provide the dependencies?
Upvotes: 6
Views: 4549
Reputation: 25704
If you're concerned about code smell, the main concern should be about having one controller calling another controller.
Controllers are meant to be called in two scenarios:
Instead, I recommend having both controllers call a business logic component that itself might use dependency injection to acquire its dependencies, and that each controller perhaps use dependency injection to acquire the business logic dependency as well.
public class HomeController : Controller {
public HomeController(IMyAppBusinessLogic bll) { ... }
}
public class WebApiController : Controller {
public WebApiController(IMyAppBusinessLogic bll) { ... }
}
public class MyAppBusinessLogic : IMyAppBusinessLogic {
public MyAppBusinessLogic(ITodoRepository repository) { ... }
}
Upvotes: 6
Reputation: 1713
Any middleware registered using app.UseServices
are available only within the scope of a web request. There is no web request context when you are trying to instantiate the webapi controller directly from your MVC app and therefore the dependencies will not be resolved.
It's normal to create an execution context manually for the purposes of unit testing. Not sure which DI framework are you using but I do something like the following in my project (OWIN not vNext) which is using SimpleInjector
public static void UseInjector(this IAppBuilder app, Container container)
{
// Create an OWIN middleware to create an execution context scope
app.Use(async (context, next) =>
{
using (var scope = container.BeginExecutionContextScope())
{
await next.Invoke();
}
});
}
Upvotes: 1