Suppose I have following structured project layer like Repository -> Service -> API from bottom to top, Code sample:
Repository:
public interface IUserInfo
{
int UID{ get; set; }
}
public class UserInfo : IUserInfo
{
public int UID { get; set; }
}
public class ProductionRepository : Repository, IProductionRepository {
public ProductionRepository(IUserInfo userInfo, StoreDbContext dbContext) : base(userInfo, dbContext)
{}
//...
}
Service:
public class ProductionService : Service, IProductionService {
public ProductionService(IUserInfo userInfo, StoreDbContext dbContext)
: base(userInfo, dbContext)
{
}
//...
}
public abstract class Service {
protected IProductionRepository m_productionRepository;
public Service(IUserInfo userInfo, StoreDbContext dbContext)
{
UserInfo = userInfo;
DbContext = dbContext;
}
protected IProductionRepository ProductionRepository
=> m_productionRepository ?? (m_productionRepository = new ProductionRepository(UserInfo, DbContext));
}
API:
public class ProductionController : Controller {
private readonly IUserInfo userInfo;
protected IProductionService ProductionBusinessObject;
public ProductionController(IUserInfo _userInfo, IProductionService productionBusinessObject)
{
userInfo = _userInfo;
ProductionBusinessObject = productionBusinessObject;
}
}
Now, in my Startup.cs I am using JWT token with "OnTokenValidated" event to grab the UserInfo information from token:
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
#region Jwt After Validation Authenticated
OnTokenValidated = async context =>
{
#region Get user's immutable object id from claims that came from ClaimsPrincipal
var userID = context.Principal.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier)
services.Configure<UserInfo>(options =>
{
options.UID = userID;
});
#endregion
},
#endregion
}
};
I am using services.Configure and try to assign the UID to the IUserInfo object, but when I debugged in my controller, the IUserInfo always represents a null object like in constructor or api method. I know I probably misuse the Dependency Injection in .Net core, so please feel free to guide me what would be the correct way to inject that IUserInfo into my Controller --> Service --> Repository, so all of them can get the actual UserInfo information!
You can inject IUserInfo
by registering it as a service in Startup.
services.AddScoped<IUserInfo>(provider =>
{
var context = provider.GetService<IHttpContextAccessor>();
return new UserInfo
{
UID = context.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier)
};
});
You cannot register things with the service collection later like that, especially not dynamically for some request. The service collection is set up once (during ConfigureServices
) and then frozen; you cannot modify it afterwards. If you want to make things available on a per-request scope, you can either share it as state in a request-scoped dependency, or put it on the HttpContext.
It does seem like a bad design though to pass around such user data as a DI dependency. You should consider passing that information explicitly in method calls.
Also, you should really embrace claims and use that directly. You can easily make some extension methods on the ClaimsPrincipal
that allows you to do User.GetUserId()
to get the user id from the claim, without having to put it into some custom object which you need to take care of. The user principal already is made available throughout the whole framework, so just use that.
Btw. note that using services.Configure<UserInfo>()
in general will not register a UserInfo
dependency (especially not an IUserInfo
dependency!) but will configure IOptions<UserInfo>
instead. But again: This won’t work in your case, since by the time the Configure()
is called, the service collection has already been built.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With