Access a Singleton Repository outside the Controller (Dependency Injection)

Asked

Viewed 190 times

0

I have a Repository instance added as Scoped in Startup and to access the instance I do the dependency injection by the Controller constructor, or by Fromservices. However, I need to access Repository within a class method, not to write the code in the Controller and Isolate the responsibilities of the application. But I cannot pass the instance by parameter in the Method, because to call it, it asks precisely the instance in the parameter. How can I capture this instance within my Class?

internal static async Task<List<WmsEstoque>> PreencheGrid([FromServices] WmsEstoqueRepository _WmsEstoqueRepository)
{
    return await _WmsEstoqueRepository.ListarIdCliente(534);
}

And what I call the method

var listaestoque = await WmsEstoque.PreencheGrid();

Error message:

There is no argument provided that corresponds to the required formal parameter "_Wmsestoquerepository" of "Wmsestoque.Preenchegrid(Wmsestoquerepository)"

1 answer

2


I believe you’re confusing the two ways and injecting dependencies.

Parameter in the constructor

As you yourself mentioned, placing a parameter in the constructor is the silver receive an instance of the repository you expect. To make this instance available to a class method we normally create a field in the class to store this received parameter in the constructor. This form of DI is possible the most common in ASP.net applications:

public class MyController : Controller // ou ApiController
{
    // campo para guardar o repositório injetado
    // usando "readonly" somente prata deixar explícito
    // que ele só será setado uma vez no construtor
    private readonly IMyRepository myRepo;

    public MyController(IMyRepository repo) 
    {
        // o construtor sempre será executado então podermos considerar
        // que myRepo sempre terá o valor esperado quando qualquer um
        // dos métodos é executado.
        this.myRepo = repo;
    }

    public List<string> GetRecords()
    {
        return this.myRepo.GetRecords().ToList();
    } 
} 

Parameter in the action (method)

Actions, which are public methods of a controller, can also receive DI instances directly with the attribute [FromService]. It is important to note that actions are not normally invoked via code, but rather by the framework when a request is made to a specific Url. That is, the responsibility to invoke actions is from the framework and he knows how to handle dependencies via attributes.

Injection of dependencies outside the controller

Another thing you commented on is to isolate the controller’s business rule. The vast majority of dependency injection libraries. Net will automatically inject the dependencies (which have been registered) into the "object tree" that are created. A simple example, imagine that you want to run some validations before adding a new object to your database. You could do something like:

public class PedidosController : ApiController
{
    private readonly IPedidoService pedidoService;
    public PedidosController(IPedidoService pedidoService)
    {
        // Se pedidoService for null, gera exceção
        this.pedidoService = pedidoService ?? throw new ArgumentNullException(nameof(pedidoService));
    }

    [HttpPost]
    public ActionResult Finalizar(PedidoDto pedidoDto)
    {
        var pedido = new Pedido { // extrai propriedades de "pedidoDto" };
        this.pedidoService.FinalizarPedido(pedido);
    }
}


public class PedidoService : IPedidoService
{
    private readonly IEstoqueRepository estoqueRepository;
    private readonly IDescontoService descontoService;
    private readonly IPedidoRepository pedidoRepository;

    // Essa classe será instanciado pelo sistema de DI com as dependências necessárias
    public PedidoService(IPedidoRepository pedidoRepository, IEstoqueRepository estoqueRepository, IDescontoService descontoService)
    {
        this.pedidoRepository = pedidoRepository ?? throw new ArgumentNullException(nameof(pedidoRepository));
        this.estoqueRepository = estoqueRepository ?? throw new ArgumentNullException(nameof(estoqueRepository));
        this.descontoService = descontoService ?? throw new ArgumentNullException(nameof(descontoService));
    }

    public void FinalizarPedido(Pedido pedido)
    {
        // Aqui você pode usar uma regra de negócio mais complexa,
        // e o melhor de duto é que essa código pode ser facilmente executado de qualquer lugar, não somente de um controller.

        this.descontoService.ValidarDescontos(pedido);

        if (!this.estoqueRepositor.IsProdutoEmEstoque(pedido.ProdutoId))
        {
            // Colocar em espera ou mandar e-mail?
        }
        else
        {
            var pedidoCriado = this.pedidoRepository.CriarPedido(pedido);
            // enviar e-mail com numero do pedido
        }
    }
}

Update for your specific case

Based on the discussion in the comments, the WsEstoque is a service that should be injected into your controller directly by the dependency injection system. For this you need to register this dependency along with the others:

// Normalmente interfaces são geradas para desacoplar consumidores da implementação de um dependência
// e também para facilitar na criação de testes unitários.
services.AddScoped<IWsEstoque, WsEstoque>();

It is common to have dozens of services registered in this way. More complex systems use more robust dependency injection libraries to facilitate the process of registering dependencies and also to generate "modules" separating different areas of the system in shape (example: Autofac, Simple Injector).

There is also a library to help you register multiple dependencies at once with the default . net core DI system, the Netcoreautoregisterdi. With it you can register all classes in your Assembly/project with a single command:

// Registra todas as classes públicas onde o nome começa com
// a string "Ws" para a sua respectiva interface.
service.RegisterAssemblyPublicNonGenericClasses()
     .Where(c => c.Name.StartsWith("Ws"))
     .AsPublicImplementedInterfaces();
  • Ipvalverde, perfect, I understood the logic, the problem is that I tried to inject by the constructor of the Class, as well as method that you made example, but, the . Net doesn’t understand my context.. The message when I instantiate in Controller (example): var wmsestoque = new Wmsestoque, it returns asking for Repository - "message": "There is no provided argument that matches the required formal parameter "Wmsestoquerepository" from "Wmsestoque.Wmsestoque(Wmsestoquerepository)"

  • If the WmsEstoque for a service/repository with dependencies in the constructor you should not be creating an instance of it manually. You just need to register it in the dependency injection system and receive it in your controller’s constructor already initialized.

  • Ipvalverde the WmsEstoque is my class where I need to do the Complex method, and within this method, I need to access the WmsEstoqueRepository, which is my data access repository.. What I’m struggling with is that even passing the WmsEstoqueRepository which was previously registered as Addscoped in Startup, and instantiating it in the Controller Builder where I call the Class method WmsEstoque, when I urge the Wmsestoque class, it asks to pass the instance of the WmsEstoqueRepository, which makes no sense to me, the instance should come injected by . Net

  • It compiles just like this: var wmsestoque =new WmsEstoque(this._WmsEstoqueRepository); var listaestoque = await wmsestoque.PreencheGrid(); How I am instantiating Wmsestoquerepository, in the Controller Constructor, and tb in the Class Constructor, the . Net would not pass the instance by context?

  • Your wsEstoque would be the equivalent of PedidoSdrvice example. You don’t need to create an instance of it, let it be injected into your Controller’s constructor

  • 1

    Thanks Ipvalderde, so I need to do the dependency injection of Wmsestap Class in Startup tb, now or for another matter, since I will have 20, 30, 40 classes in this condition maybe, and would not need to instantiate all as Scoped, since maybe the user can only use one of these... Thank you!

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.