Search code examples
c#autofacprincipal

Autofac, Users and Logging


I am using Autofac in my project and for the most part it works fine. A while ago, I needed to get access to the current user and was told the best way to do this was to create a wrapper class like this:

public class PrincipalProvider : IPrincipalProvider
{
    public IPrincipal User => HttpContext.Current?.User;
}

This has worked without any issues through my application. I now have a new provider which looks like this:

public class WmsProvider : IWmsProvider
{
    private readonly Lazy<ILogProvider> _logProvider;
    private readonly Lazy<IMessageProvider> _messageProvider;

    private readonly CormarConfig _config;
    private readonly ClaimsIdentity _identity;

    public WmsProvider(IPrincipalProvider principalProvider, Lazy<ILogProvider> logProvider, Lazy<IMessageProvider> messageProvider)
    {
        _messageProvider = messageProvider;
        _logProvider = logProvider;

        _identity = (ClaimsIdentity)principalProvider.User.Identity;
    }

    /// <summary>
    /// Sends the order to WMS
    /// </summary>
    /// <param name="model">The order model</param>
    public async Task SendAsync(OrderViewModel model)
    {
        var request = WmsFactory.Create(model);
        await _logProvider.Value.TraceAsync($"This is a test", _identity);
        await _messageProvider.Value.CreateAsync(request, model.OrderNumber, MessageType.Wms, "ORD", Method.POST, null);
    }
}

(I have stripped out the rest of the code for brevity)

In this case, the User is null and throws an error (Object instance not found). But I have another class with a similar constructor:

    public OrderProvider(CormarConfig config, IOrderService orderSerivce, IPrincipalProvider principalProvider, Lazy<IAccountProvider> accountProvider, Lazy<ICollectionManagerProvider> collectionManagerProvider, Lazy<IEmailProvider> emailProvider, Lazy<IJournalProvider> journalProvider, Lazy<IOrderLineProvider> orderLineProvider, Lazy<IStockProvider> stockProvider, Lazy<webServices> webService, Lazy<ITroposOrderLineService> troposOrderLineService, Lazy<ITroposOrderService> troposOrderService, Lazy<ITroposUnitOfWork> troposUnitOfWork, Lazy<IWmsProvider> wmsProvider)
    {
        //Assign our config
        _config = config;

        // Add our services to our class
        _connectionType = config.ConnectionType;
        _orderService = orderSerivce;

        // Add our providers to our class
        _identity = (ClaimsIdentity)principalProvider.User.Identity;

        // Add our optional providers
        _accountProvider = accountProvider;
        _collectionManagerProvider = collectionManagerProvider;
        _emailProvider = emailProvider;
        _journalProvider = journalProvider;
        _orderLineProvider = orderLineProvider;
        _stockProvider = stockProvider;
        _webService = webService;
        _wmsProvider = wmsProvider;
        _troposOrderLineService = troposOrderLineService;
        _troposOrderService = troposOrderService;
        _troposUnitOfWork = troposUnitOfWork;
    }

And this works fine. Both are registered the same way in my Module:

builder.RegisterType<OrderProvider>().As<IOrderProvider>().InstancePerRequest();
builder.RegisterType<WmsProvider>().As<IWmsProvider>().InstancePerRequest();

builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>();

One thing to note, is that WmsProvider is injected into OrderProvider, so it is not directly injected into the controller. The controller constructor looks like this:

public OrdersController(IOrderProvider provider)
{
    _provider = provider;
}

This might be where the issue lies. In a dependent, is the context not available? If it isn't what is the solution? Is there a way to get to the context from the child?

Any help would be appreciated.


Solution

  • I figured this out. It was as I said, the context was not available in the nested classes, so to fix this I changed the registration of the PrincipalProvider to instance per request:

    builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>().InstancePerRequest();
    

    And I changed the PrincipalProvider to look like this:

    public class PrincipalProvider : IPrincipalProvider
    {
    
        // Readonly fields
        private readonly HttpContext _current;
    
        /// <summary>
        /// Default constructor
        /// </summary>
        public PrincipalProvider()
        {
            _current = HttpContext.Current;
        }
    
        /// <summary>
        /// Gets the current user
        /// </summary>
        public IPrincipal User => _current?.User;
    }
    

    And this fixed my issue.