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.
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.