Warm tip: This article is reproduced from serverfault.com, please click

Inheriting logging services from base class

发布于 2020-12-01 16:30:11

I have a .Net Core 3.1 API controller with a constructor that looks like this:

public class MachineListsController : ControllerBase
{
    private readonly jiWeb_ProdContext _context;
    private readonly ILogger _logger;
    private readonly ILoggingMessageService _loggingMessage;

    public MachineListsController(jiWeb_ProdContext context, ILogger<MachineListsController> logger, ILoggingMessageService loggingMessage)
    {
        _context = context;
        _logger = logger;
        _loggingMessage = loggingMessage;
    }

    public string Message { get; set; }

   ...
}

You can see that I am injecting a .Net Core logging service and the database context into it.

Then I use the logging like this in my controller methods:

    [HttpGet("FactoryMachines/{factoryId}")]
    public async Task<ActionResult<IEnumerable<MachineList>>> GetMachinesForFactory(Guid factoryId)
    {
        var machineList = await _context.MachineList.Where(n => n.FactoryId == factoryId).ToListAsync();

        Message = _loggingMessage.GetLogSuccess(this.GetType().Name.ToString(), ControllerActions.GetAction, "FactoryMachines", factoryId.ToString());
        _logger.LogInformation(Message);
        return machineList;
    }

The logging is working great, but I'm realizing that I should create a base class that handles logging so I don't have to add or change it inside of every controller I write.

So I started to write this base controller:

[ApiController]
public class MyBaseController : ControllerBase
{
    readonly jiWeb_ProdContext _context;
    readonly ILogger _logger;
    readonly ILoggingMessageService _loggingMessage;

    public BaseController(jiWeb_ProdContext context, ILogger<BaseController> logger, ILoggingMessageService loggingMessage)
    {
        _context = context;
        _logger = logger;
        _loggingMessage = loggingMessage;
    }

}

Then I changed my controller to inherit from it like this:

public class MachineListsController : MyBaseController
{
    [HttpGet("FactoryMachines/{factoryId}")]
    public async Task<ActionResult<IEnumerable<MachineList>>> GetMachinesForFactory(Guid factoryId)
    {
        var machineList = await _context.MachineList.Where(n => n.FactoryId == factoryId).ToListAsync();

        return machineList;
    }
}

But I'm getting error and I'm unsure of what to do on the next step.

Here's the error:

There is no argument given that corresponds to the required formal parameter 'context' of 'BaseController.BaseController(jiWeb_ProdContext, ILogger<BaseController>, ILoggingMessageService)'

Specifically, how do I set up my controllers so that they can just use the base class for logging so I don't have to write logging code for every new controller action I create?

Thanks!

Questioner
SkyeBoniwell
Viewed
0
Brando Zhang 2020-12-03 10:26:23

As far as I know, if the base class constructor method contains value, we should pass it in the subclass constructor method and also you should follow Nkosi comment to modify the property to protected.

More details, you could refer to below codes:

[ApiController]
public class MyBaseController : ControllerBase
{
    protected readonly ILogger _logger;

    public MyBaseController(ILogger<MyBaseController> logger)
    {
    
        _logger = logger;

    }

}

[Route("api/[controller]")]
public class MachineListsController : MyBaseController
{
    public MachineListsController(ILogger<MyBaseController> logger) :base(logger)
    {
    
    }


    [HttpGet]
    public IActionResult Get() {
         _logger.Log(Microsoft.Extensions.Logging.LogLevel.Trace,"aaa" );

        return Ok();
    }
}

I am wondering, would there be a way to do the logging in the base class? Like where you call _logger.Log in the MachineListsController class, could that be moved to base?

As far as I know, we could only add logs before the MachineListsController's action executed or after the MachineListsController's action executed.

If this match your requirement, you could try to use action filter.

You could add iactionfilter interface to the basecontroller and overried the OnActionExecuted and OnActionExecuting method.

More details, you could refer to below codes:

[Route("api/[controller]")]
[ApiController]
public class MyBaseController : ControllerBase, IActionFilter
{
    protected readonly ILogger _logger;

    public MyBaseController(ILogger<MyBaseController> logger)
    {
    
        _logger = logger;

    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        _logger.Log(Microsoft.Extensions.Logging.LogLevel.Trace, "aaa");
        int i = 0;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.Log(Microsoft.Extensions.Logging.LogLevel.Trace, "bbb");
        int i = 0;
    }
}

Result:

enter image description here