1

I am working on an ASP Core 2 project using JWT authentication and the Dapper ORM.

Like all ASP projects, I have a lot of controllers, each instantiating its associated data objects. Each data object inherits from an abstract DbObject class that provides database access services. I also have an AuthenticatedUser object that abstracts the JWT to make it's properties easier to use.

What I want is to do is create the AuthenticatedUser object in the constructor of DbObject. Of course, one method is to create it in the controller and pass it to every concrete data object but this is messy as it would have to be passed hundreds of times (and it just feels wrong).

Is there a way to use the ASP Core middleware to get the token after authentication and make it available through dependency injection in the DbObject?


Edit Hopefully, this clarifies my intentions. I would like the controller to create data objects and use their properties and methods without regard to implementation (i.e. DbObject). But queries executed by DbObject will be filtered by information in the token of the logged in user.

public class ManufacturerController : Controller {

    [HttpGet]
    public async Task<IActionResult> Get() {
        var manufacturers = await new Manufacturer().SelectMany();
        return Ok(manufacturers);
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> Get(int id) {
        var manufacturer = await new Manufacturer().SelectOne(id);
        return Ok(manufacturer);
    }...

public class Manufacturer : DbObject<Manufacturer> {

    protected override string QrySelectOne => @"  
        Select * 
        From org.fn_Manufacturers ({0}) 
        Where Id = {1}";

    protected override string QrySelectMany => @" 
        Select * 
        From org.fn_Manufacturers ({0})";

    public int Id { get; set; }
    public string Name { get; set; }
    public string Phone { get; set; }...

public abstract class DbObject<T> {

    protected readonly AuthenticatedUser authenticatedUser;

    public DbObject(IHttpContextAccessor contextAccessor) {
        authenticatedUser = new 
            AuthenticatedUser(contextAccessor.HttpContext.User);
    }

    protected abstract string QrySelectOne { get; }
    protected abstract string QrySelectMany { get; }

    public async Task<T> SelectOne (int id) {...}
    public async Task<T> SelectOne(params object[] ids) {...}

    public async Task<IEnumerable<T>> SelectMany () {...}
    public async Task<IEnumerable<T>> SelectMany (params object[] ids) {...}

I suppose one solution may be to create a static data object factory which has the IHttpContextAccessor injected??

CodeFuller
  • 30,317
  • 3
  • 63
  • 79
bob
  • 579
  • 1
  • 8
  • 22

1 Answers1

0

ASP.NET Core provides IHttpContextAccessor interface for accessing HttpContext from non-controller objects.

The usage is fair simple. Inject IHttpContextAccessor into DbObject and access HttpContext by calling IHttpContextAccessor.HttpContext:

public abstract class DbObject
{
    protected DbObject(IHttpContextAccessor contextAccessor)
    {
        var context = contextAccessor.HttpContext;

        //  Create instance of AuthenticatedUser based on context.User or other request data
    }
}

EDIT

Your controllers instantiate data objects directly (with new operator), that's why you can't have IHttpContextAccessor injected out of the box. Here are possible solutions. I list them in order of my preference (from best to worst).

  1. If each controller uses only one (or just several) types of data objects, the best options will be to avoid direct instantiation and move toward normal Dependency Injection.

    So if ManufacturerController requires only Manufacturer like in your sample then, it's better to inject Manufacturer instance to controller, not to create it inside:

    public class Manufacturer1Controller : Controller
    {
        private readonly Manufacturer manufacturer;
    
        public Manufacturer1Controller(Manufacturer manufacturer)
        {
            this.manufacturer = manufacturer ?? throw new ArgumentNullException(nameof(manufacturer));
        }
    
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            var manufacturers = await manufacturer.SelectMany();
            return Ok(manufacturers);
        }
    
        //  ...
    }
    

    IHttpContextAccessor will be injected into Manufacturer and passed to base DbObject:

    public class Manufacturer : DbObject<Manufacturer>
    {
        public Manufacturer(IHttpContextAccessor contextAccessor) : base(contextAccessor)
        {
        }
    }
    

    It's the cleanest solution in the list. You use DI in classic way and utilize all benefits DI provides.

  2. If one controller could use dozens of different data objects, you could inject the factory object that creates instances of data objects. It could be simple implementation based on IServiceProvider:

    public interface IDbObjectFactory
    {
        TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>;
    }
    
    public class DbObjectFactory : IDbObjectFactory
    {
        private readonly IServiceProvider serviceProvider;
    
        public DbObjectFactory(IServiceProvider serviceProvider)
        {
            this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
        }
    
        public TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>
        {
            return serviceProvider.GetRequiredService<TDbObject>();
        }
    }
    
    public class Manufacturer2Controller : Controller
    {
        private readonly IDbObjectFactory dbObjectFactory;
    
        public Manufacturer2Controller(IDbObjectFactory dbObjectFactory)
        {
            this.dbObjectFactory = dbObjectFactory ?? throw new ArgumentNullException(nameof(dbObjectFactory));
        }
    
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            var manufacturer = dbObjectFactory.Create<Manufacturer>();
            var manufacturers = await manufacturer.SelectMany();
            return Ok(manufacturers);
        }
    }
    

    The code for Manufacturer and DbObject does not change comparing to the first option.

    I don't see any reason not to use option #1 or #2. However just to complete the picture, I'll describe another two options.

  3. Inject IHttpContextAccessor into conroller and pass this instance (or IHttpContextAccessor.HttpContext.User) to Data Object constructor invoked with operator new:

    public class Manufacturer3Controller : Controller
    {
        private readonly IHttpContextAccessor contextAccessor;
    
        public Manufacturer3Controller(IHttpContextAccessor contextAccessor)
        {
            this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
        }
    
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            var manufacturer = await new Manufacturer(contextAccessor).SelectMany();
            //  or
            //  var manufacturer = await new Manufacturer(contextAccessor.HttpContext.User).SelectMany();
            return Ok(manufacturer);
        }
    }
    

    It's a bad solution, because you don't use Dependency Injection for Manufacturer here and loose many advantages that DI provides.

  4. And the worst option would be using of static object factory with injected IHttpContextAccessor. With this approach you also loose benefits of DI. In addition you get ugly code somewhere in Startup that initializes static instance of IHttpContextAccessor. When you come to this approach, you'll discover that theere is no quite elegant way to do this.

My advice: use option #1 untill you have good reasons against it. Then use option #2.

Here is Sample Project on GitHub with samples for approaches ##1-3.

CodeFuller
  • 30,317
  • 3
  • 63
  • 79
  • Nice, but I'm missing something. With a parameterless constructor in the abstract class, my concrete classes need to have parameterless constructors also, which means the controllers need to create them by passing the IHttpContextAccessor. This would defeat the purpose of using dependency injection. – bob Apr 13 '18 at 01:18
  • You asked: "... get the token after authentication and make it available through **dependency injection in the DbObject**". DI implies that you don't create instances of data objects by yourself (with `new` operator) but inject them into your controller. So you will not actually create data objects and pass `IHttpContextAccessor` to their constructor. DI container will do it for you. If described approach is not what you need, please update your question with pseudo-code of desired behavior, both for controller and data object. – CodeFuller Apr 13 '18 at 04:30
  • Thank you, that makes a lot of sense! Thanks for all the detail, too. My controllers use 1 or 2 objects so I'll go with #1. – bob Apr 17 '18 at 00:14