Dependency Injection when controller called from another controller - c#

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?

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:
By the system (i.e. MVC)
By your unit tests
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) { ... }
}

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();
}
});
}

Related

Get DbContext in async action

I am rewriting/ moving a website from ASP MVC to ASP MVC Core. This application has a dynamic menu which depends on the logged in user. In order to build the menu, each controller derives from a custom BaseController who sets in ViewBag menu items, an later, in Layout, those items are retrieved and passed as arguments to a PartialView.
public BaseController:Controller
{
public BaseController()
{
...
ViewBag.Menu=Utils.GetMenu();
...
}
}
I don't want to use the same logic as the lads who wrote the old code. So I thought to use a ViewComponent to render the menu. But I have a problem with this approach. In Invoke method I need to query for the menu items. Right now I get a DbContext instance from the service provider (HttpContext.RequestServices), and I use it to query whatever data I need. But the Invoke function is called asynchronously from Layout and I know that it is not very good to send DbContext to async methods:
<cache expires-after="#TimeSpan.FromHours(2)" enabled="true">
#await Component.InvokeAsync(typeof(Admin.ViewComponents.MeniuViewComponent))
</cache>
Is this a good approach? Is it safe to get a DbContext (registered as Scoped in Startup) in Invoke method (or any other async method or action) and use it? And if it is not a good idea, how should I deal with this kind of situations where I need data from db in async methods?
I had the case in my project to get the DbContext inside a HostedService from asp.net core.
I injected the IServiceProvider inside the constructor and build my own scope, to get the registered DbContext:
private readonly IServiceProvider _serviceProvider;
public BaseController(IServiceProvider provider){
_serviceProvider = provider;
}
private void DoSth(){
using var scope = _serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<YOUR_DbContext_CLASS>();
... //do something great stuff here with the database
}
I think you can directly dependency inject the DbContext in the ViewComponent
public class MenuViewComponent : ViewComponent
{
private readonly MenuDbContext _context;
public TopMenuViewComponent(MenuDbContext context)
{
_context = context;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var model = _context.MenuItems.ToList();
return View("Default", model);
}
}
For more details, refer to the doc.

Automatic controller detection in asp .net core

Asp .net core MVC automatically detects controllers in the project at startup. I've been looking for a way to prevent this for certain Controllers. As a result, I figured out that I could implement IControllerFactory to filter out controllers dynamically. However, as I understand, it is for Controller creation, not detection. Is there any other way I could do this without implementing either IControllerFactory or the IControllerActivator? Is there any other component which involves in controller detection at the startup?
IControllerActivator is used by IControllerFactory for the controller creation.
You need to implement your own IControllerActivator and add your logic into there.
I'd suggest adding an attribute to the Controller, and the using reflection in the Create method to enable/disable the controller
public class CustomControllerResolver : IControllerActivator
{
public object Create(ControllerContext actionContext)
{
var actionDescriptor = actionContext.ActionDescriptor;
var controllerType = actionDescriptor.ControllerTypeInfo.AsType();
return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
}
public virtual void Release(ControllerContext context, object controller)
{
}
}
Register your custom resolver in the ServicesCollection
services.Replace(ServiceDescriptor.Transient<IControllerActivator, CustomControllerResolver>());
I found a way to keep some Controllers from being registered. We can register a new convention that searches for controllers need to be removed and pop them from the controllers' list.
public class ApplicationDescription : IApplicationModelConvention
{
public ApplicationDescription()
{
}
public void Apply(ApplicationModel application)
{
var ctr = application.Controllers.Where((model) => {
return model.ControllerType.IsEquivalentTo(typeof(IgnoredController));
});
if (ctr.Count() > 0)
{
foreach (var controller in ctr.ToList())
{
application.Controllers.Remove(controller);
}
}
}
}
Register the new convention with MVC
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc((options)=> {
options.Conventions.Add(new ApplicationDescription());
options.Conventions.Add(new ControllerDescriptionAttribute("aa"));
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
}

Correct way to connect controllers using Dependency Injection

If i have a controller that receives and processes the action selected by the user, and then want to use another controller to store all database related logic, what is the correct way to connect these controllers while allowing the 2nd controller to interact with the database context.
At the moment I have it working with creating a database context in the first controller and then parsing that to the database controller when I connect the two using DI, but hopefully someone could show me the correct way to do this.
public class TestController : Controller
{
private readonly DatabaseContext context;
private Database.UserController userDatabaseController;
public TestController(DatabaseContext db)
{
context = db;
userDatabaseController = new Database.UserController(context);
}
}
database controller
public class UserController : Controller
{
private readonly DatabaseContext context;
public UserController(DatabaseContext ctx)
{
context = ctx;
}
public bool RegisterUser(Models.DatabaseModels.UserModel model)
{
try
{
context.Users.Add(model);
context.SaveChanges();
return true;
}
catch (Exception e)
{
return false;
}
}
}
startup.cs
services.AddDbContext<DatabaseContext>
(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
databasecontext
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options)
{ }
public DbSet<DatabaseModels.UserModel> Users { get; set; }
}
The "correct" way is: you don't. A controller should never directly call into another controller. You can redirect to a new route that maps to a different controller, but that's it. If you have some common logic that needs to be shared, then that should be factored out into a completely different class that both controllers can utilize.
If you're finding that you need to call Controller methods from another Controller, you probably need to refactor your code. Controllers should have very little logic in them, which usually just involves calling a Service layer and then constructing a ViewModel from the data.
My advice would be to do some reading on the Service Layer pattern and the Repository pattern (sometimes called the Manager pattern).

Asp.net Core 2 - How to use ServiceLocator in Asp.net Core 2.0

My Startup is like this :
public void ConfigureServices(IServiceCollection services)
{
// code here
Bootstraper.Setup(services);
}
And my Bootstraper class is like this :
public static partial class Bootstraper
{
// code here
public static IServiceCollection CurrentServiceCollection { get;set;}
public static IServiceProvider CurrentServiceProvider
{
get { return CurrentServiceCollection.BuildServiceProvider(); }
}
public static void Setup(IServiceCollection serviceCollection)
{
// code here
SetupLog();
InitializeCulture();
InitializeDbContexts();
RegisterDataModelRepositories();
}
and this is content of my RegisterDataModelRepositories():
CurrentServiceCollection.AddTransient<IDefAccidentGroupRepository>(p => new DefAccidentGroupRepository(ApplicationMainContextId));
CurrentServiceCollection.AddTransient<IDefGenderRepository>(p => new DefGenderRepository(ApplicationMainContextId));
in short : I just want to be able to use Service Locator in my methods without resolving dependency in class constructor ... is there any way around it ....
Dependency injection can also be done on a by action basis.
Referece Dependency injection into controllers: Action Injection with FromServices
Sometimes you don't need a service for more than one action within your controller. In this case, it may make sense to inject the service as a parameter to the action method. This is done by marking the parameter with the attribute [FromServices]
public IActionResult SomeAction([FromServices] IReportService reports) {
//...use the report service for this action only
return View();
}
Just make sure that the required services are registered with the service collection.
services.AddTransient<IDefAccidentGroupRepository>(p => new DefAccidentGroupRepository(ApplicationMainContextId));
services.AddTransient<IDefGenderRepository>(p => new DefGenderRepository(ApplicationMainContextId));
services.AddTransient<IReportService, ReportService>().
well , thanks for your help ...
There is a easier and better way for it , I just need to add another Service that use these repository and then resolve that service in my controller and let Asp.net Core 2.0 DI to solve the problem for me ...
public interface IActionService
{
IRepositoryA repA {get;set;}
IRepositoryB repB { get;set;}
DoTaskX();
DoTaskY();
}
then in my ActionService :
public class ActionService : IActionService
{
public IRepositoryA repA {get;set;}
public IRepositoryB repB { get;set;}
public ActionService (IRepositoryA rep_a , IRepositoryB rep_b ) {
repA = rep_a;
repB = rep_b;
}
DoTaskX(){
// do task using repository A and B
}
}
then I register IActionService in Startup.cs and resolve itin my ActionController and life become easier and code become cleaner ...
the solution was easy but I had to change my mindset to solve the problem ...

Dependency Injection pass parameters by constructor

We have a project where we need to use DI and ASP Core.
I'm very new to this and have a question.
I have a controller named HomeController like this:
public class HomeController : BaseController {
private IOrderService _orderService;
public HomeController(IOrderService orderService) {
_orderService = orderService;
}
public IActionResult Index() {
var orders = _orderService.GetMyOrders();
return View(orders);
}
}
The code looks like this:
public class OrderService : BaseService, IOrderService {
public OrderService(IDataContextService dataContextService) {
_dataContextService = dataContextService;
}
public List<Orders> GetMyOrders() {
var orders = // do my code here which works fine!;
// here i need some code do check orders for delivery so
DeliveryService deliveryService = new DeliveryService(_dataContextService);
// update my orders and return these orders
return orders;
}
}
public class DeliveryService : BaseService, IDeliveryService {
public DeliveryService(IDataContextService dataContextService) {
_dataContextService = dataContextService;
}
public void MyMethod() {
}
}
public class BaseService {
protected IDataContextService _dataContextService;
}
Almost all my services have a constructor like the OrderService and DeliveryService. My question is, do I have to pass the _dataContextService every time, or is there a solution within the dependency pattern?
You should keep it the way you have it and asp.net core IoC will inject it for you, but make sure it is injected per request, this will help to insantiate only one context for each request and dispose it after the request is served.
You can register the context and services in the ConfigureServices method inside the Startup class as below
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
// Add application services.
services.AddTransient<HomeController>();
services.AddTransient<IOrderService , OrderService >();
services.AddTransient<IDeliveryService, DeliveryService>();
services.AddScoped<IDataContextService , YourDataContextService >();
}
The AddScoped method will create only one instance of the object for each HTTP request
If I understand correctly what you are asking, you are looking for an IoC container. .NET Core has built in support for dependency injection. Basically, you just indicate which implementation should be provided when an interface is requested. Then the container will instantiate the types for you. See for example https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection.
Hope that helps

Categories

Resources