web-dev-qa-db-fra.com

ASP.Net Core Appeler un contrôleur depuis un autre contrôleur

Dans ma solution ASP.Net Core MVC 6, je dispose de deux ensembles de contrôleurs. Un ensemble contient les pages Web avec leurs vues habituelles. Un autre jeu contient les contrôleurs API.

Pour éviter la duplication de la logique de la base de données, les contrôleurs Web utilisent les contrôleurs API. Actuellement, je crée manuellement une instance du contrôleur requis en lui donnant un DbContext en tant qu'argument constructeur. C'est le DbContext donné au contrôleur Web par injection de dépendance.

Mais chaque fois que j'ajoute un autre paramètre de constructeur au contrôleur API, je dois modifier tous les contrôleurs Web utilisant ce contrôleur API.

Comment utiliser le système d'injection de dépendance intégré à ASP.Net 5 pour créer une instance du contrôleur d'API requis pour moi? Ensuite, les paramètres de constructeur requis seront automatiquement renseignés.

Une solution pourrait consister à déplacer la logique de base de données des contrôleurs API vers une couche distincte et à l'appeler à partir des contrôleurs API et Web. Cela ne résoudrait pas mon problème car la nouvelle couche aurait toujours besoin des mêmes paramètres et je ne suis pas fan du câblage inutile.

Une autre solution consisterait à faire en sorte que les contrôleurs Web accèdent à l'API via un appel Web, mais cela ne ferait qu'ajouter à la complexité de l'application.

Aujourd'hui je fais ceci:

public IActionResult Index()
{
    using (var foobarController = new Areas.Api.Controllers.FoobarController(
        // All of these has to be in the constructor of this controller so they can be passed on to the ctor of api controller
        _dbContext, _appEnvironment, 
        _userManager, _roleManager, 
        _emailSender, _smsSender))
    {
        var model = new IndexViewModel();
        model.Foo = foobarController.List(new FoobarRequest() { Foo = true, Bar = false });
        model.Bar = foobarController.List(new FoobarRequest() { Foo = false, Bar = true });
        return View(model);
    }
}

Et j'espère quelque chose comme ceci: (Cet exemple ne fonctionne pas.)

using (var foobarController = CallContextServiceLocator.Locator.ServiceProvider.GetService<Areas.Api.Controllers.FoobarController>())
{
    var model = new IndexViewModel();
    model.Foo = foobarController.List(new FoobarRequest() { Foo = true, Bar = false });
    model.Bar = foobarController.List(new FoobarRequest() { Foo = false, Bar = true });
    return View(model);
}
10
Tedd Hansen

Comment utiliser le système d'injection de dépendance intégré à ASP.Net 5 pour créer une instance du contrôleur d'API requis pour moi? 

Dans votre Startup.cs, vous pouvez indiquer au MVC d’enregistrer tous vos contrôleurs en tant que services .

services.AddMvc().AddControllersAsServices();

Ensuite, vous pouvez simplement injecter le contrôleur souhaité dans votre autre contrôleur via le mécanisme DI et appeler sa méthode d'action.

15
B12Toaster

Pour pouvoir utiliser un contrôleur d'un autre contrôleur, vous devez:

  1. Enregistrez le contrôleur dans Startup.cs ConfigureServices: services.AddTransient <Areas.Api.Controllers.FoobarController, Areas.Api.Controllers.FoobarController>(); 
  2. Vous devez transmettre le contrôleur auquel vous souhaitez accéder en tant que paramètre ctor au contrôleur principal.

Si vous devez accéder à des propriétés locales du contrôleur, telles que User ou Url, vous pouvez le faire de deux manières.

La première consiste à utiliser DI pour qu'une instance de IHttpContextAccessor puisse accéder à User et IUrlHelper à des objets Url:

public class FoobarController : Controller
{
    private readonly ApplicationDbContext _dbContext;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IUrlHelper _urlHelper;
    public FoobarController(ApplicationDbContext dbContext, IHttpContextAccessor httpContextAccessor, IUrlHelper _urlHelper, [...])
    {
         _dbContext = dbContext;
         _httpContextAccessor = httpContextAccessor;
         _urlHelper = urlHelper;
    }

    public FoobarResponse List(FoobarRequest request)
    {
        var userId = _httpContextAccessor.HttpContext.User.GetUserId();
        var response = new FoobarResponse();
        response.List = _dbContext.Foobars.Where(f => f.UserId == userId).ToList();
        response.Thumb = 
        return response;
    }
}

La deuxième façon est de le définir dans le contrôleur appelant:

public class HomeController : Controller
{
    private Areas.Api.Controllers.FoobarController _foobarController;
    public HomeController(Areas.Api.Controllers.FoobarController foobarController)
    {
        _foobarController = foobarController;
    }

    private void InitControllers()
    {
        // We can't set this at Ctor because we don't have our local copy yet
        // Access to Url 
        _foobarController.Url = Url;
        // Access to User
        _foobarController.ActionContext = ActionContext;
        // For more references see https://github.com/aspnet/Mvc/blob/6.0.0-rc1/src/Microsoft.AspNet.Mvc.ViewFeatures/Controller.cs
        // Note: This will change in RC2
    }

    public IActionResult Index()
    {
        InitControllers();

        var model = new IndexViewModel();
        model.Foo = _foobarController.List(new FoobarRequest() { Foo = true, Bar = false });
        model.Bar = _foobarController.List(new FoobarRequest() { Foo = false, Bar = true });
        return View(model);
    }
}

Le code source du contrôleur ASP.Net Core MVC6 RC1 peut être trouvé ici . Il est cependant en train de subir une réécriture lourde pour RC2 et avec lui les propriétés à copier pour accéder à User et l’URL changera.

7
Tedd Hansen

Ne le fais pas. Déplacez cette logique vers un autre composant partagé entre les 2 contrôleurs.

3
davidfowl

Pourquoi votre nouvelle couche aurait-elle besoin de câblage? Pourquoi ne pas prendre un objet dans les deux contrôleurs et appeler une méthode sur cet objet. Le conteneur DI pourrait résoudre les dépendances de ce nouvel objet sans câblage en double, n'est-ce pas?

c'est à dire que vous pourriez avoir ceci:

public class MvcController
{
    SharedComponent sharedComponent;
    public MvcController(SharedComponent sharedComponent)
    {
        this.sharedComponent = sharedComponent;
    }
    public IActionResult Index()
    {
        var model = new IndexViewModel();
        model.Foo = shredComponent.List(new FoobarRequest() { Foo = true, Bar = false });
        model.Bar = shredComponent.List(new FoobarRequest() { Foo = false, Bar = true });
        return View(model);   
    }
}

// Répétez cette opération pour le contrôleur API

public class SharedComponent
{
    public SharedComponent(DBContext dbContext, AppEnvironment appEnvironment, UserManager userManager, RoleManager roleManager, 
        EmailSender emailSender, SmsSender smsSender)
    {
       ...Store in fields for later usage
    }
}
0
Sam Holder