Returning null on httpContextAccessor.HttpContext
HttpContext
is only valid during a request. When .NET Core creates an ApplicationDbContext
class for the call to Configure
there is no valid context.
You need to store a reference to the IHttpContextAccessor
in your DbContext
constructor and then you can use that variable to access the HttpContext
property in your OnBeforeSaving()
method.
For example:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options,
IHttpContextAccessor httpContextAccessor
)
: base(options)
{
_httpContextAccessor = httpContextAccessor;
}
....
}
Then, in your OnBeforeSaving() method:
private void OnBeforeSaving()
{
var userName = _httpContextAccessor.HttpContext.User.Identity.Name;
...
}
Think of HttpContext
as a telephone call. If you pick the phone up when no-one has called then there is no context
i.e. it is null. When someone does call then you have a valid context
. This is the same principal for a web call. The Configure
method in Startup is not a web call and, as such, does not have a HttpContext
.
From another site:
HttpContext object will hold information about the current http request. In detail, HttpContext object will be constructed newly for every request given to an ASP.Net application and this object will hold current request specific informations like Request, Response, Server, Session, Cache, User and etc. For every request, a new HttpContext object will be created which the ASP.Net runtime will use during the request processing. A new HttpContext object will be created at the beginning of a request and destroyed when the request is completed.
Celsius
Updated on July 16, 2022Comments
-
Celsius almost 2 years
We override SaveChangesAsync() to update automatically for DateCreated, CreatedBy, LastDateModified and LastModifiedBy. With CreatedBy and LastModifiedBt, we need to the User Id of Identity.
In our constructor for ApplicationDbContext, we've added something like this:
_userName = httpContextAccessor.HttpContext.User.Identity.Name; //_userID = userManager.GetUserId(httpContext.HttpContext.User);
.. and always get the null in this httpContextAccessor.HttpContext. Any ideas? We included the source below.
Environment:
.NET Core 2.1
SQL Server
ApplicationDBContext.cs:
using System; using System.Collections.Generic; using System.Text; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using AthlosifyWebArchery.Models; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Linq.Expressions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; namespace AthlosifyWebArchery.Data { public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string> { private readonly string _userID; private readonly string _userName; public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor httpContextAccessor ) : base(options) { _userName = httpContextAccessor.HttpContext.User.Identity.Name; //_userID = userManager.GetUserId(httpContext.HttpContext.User); } public DbSet<AthlosifyWebArchery.Models.TournamentBatchItem> TournamentBatchItem { get; set; } public DbSet<AthlosifyWebArchery.Models.TournamentBatch> TournamentBatch { get; set; } public virtual DbSet<AthlosifyWebArchery.Models.Host> Host { get; set; } public DbSet<AthlosifyWebArchery.Models.HostApplicationUser> HostApplicationUser { get; set; } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); foreach (var entityType in builder.Model.GetEntityTypes()) { // 1. Add the IsDeleted property entityType.GetOrAddProperty("IsDeleted", typeof(bool)); // 2. Create the query filter var parameter = Expression.Parameter(entityType.ClrType); // EF.Property<bool>(post, "IsDeleted") var propertyMethodInfo = typeof(EF).GetMethod("Property").MakeGenericMethod(typeof(bool)); var isDeletedProperty = Expression.Call(propertyMethodInfo, parameter, Expression.Constant("IsDeleted")); // EF.Property<bool>(post, "IsDeleted") == false BinaryExpression compareExpression = Expression.MakeBinary(ExpressionType.Equal, isDeletedProperty, Expression.Constant(false)); // post => EF.Property<bool>(post, "IsDeleted") == false var lambda = Expression.Lambda(compareExpression, parameter); builder.Entity(entityType.ClrType).HasQueryFilter(lambda); } // Many to Many relationship builder.Entity<HostApplicationUser>() .HasKey(bc => new { bc.HostID, bc.Id }); builder.Entity<HostApplicationUser>() .HasOne(bc => bc.Host) .WithMany(b => b.HostApplicationUsers) .HasForeignKey(bc => bc.HostID); builder.Entity<HostApplicationUser>() .HasOne(bc => bc.ApplicationUser) .WithMany(c => c.HostApplicationUsers) .HasForeignKey(bc => bc.Id); } public override int SaveChanges(bool acceptAllChangesOnSuccess) { OnBeforeSaving(); return base.SaveChanges(acceptAllChangesOnSuccess); } public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken)) { OnBeforeSaving(); return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); } private void OnBeforeSaving() { // Added var added = ChangeTracker.Entries().Where(v => v.State == EntityState.Added && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList(); added.ForEach(entry => { ((IBaseEntity)entry.Entity).DateCreated = DateTime.UtcNow; ((IBaseEntity)entry.Entity).CreatedBy = _userID; ((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow; ((IBaseEntity)entry.Entity).LastModifiedBy = _userID; }); // Modified var modified = ChangeTracker.Entries().Where(v => v.State == EntityState.Modified && typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList(); modified.ForEach(entry => { ((IBaseEntity)entry.Entity).LastDateModified = DateTime.UtcNow; ((IBaseEntity)entry.Entity).LastModifiedBy = _userID; }); // Deleted //var deleted = ChangeTracker.Entries().Where(v => v.State == EntityState.Deleted && //typeof(IBaseEntity).IsAssignableFrom(v.Entity.GetType())).ToList(); var deleted = ChangeTracker.Entries().Where(v => v.State == EntityState.Deleted).ToList(); deleted.ForEach(entry => { ((IBaseEntity)entry.Entity).DateDeleted = DateTime.UtcNow; ((IBaseEntity)entry.Entity).DeletedBy = _userID; }); foreach (var entry in ChangeTracker.Entries() .Where(e => e.State == EntityState.Deleted && e.Metadata.GetProperties().Any(x => x.Name == "IsDeleted"))) { switch (entry.State) { case EntityState.Added: entry.CurrentValues["IsDeleted"] = false; break; case EntityState.Deleted: entry.State = EntityState.Modified; entry.CurrentValues["IsDeleted"] = true; break; } } } } }
Startup.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using AthlosifyWebArchery.Data; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using AthlosifyWebArchery.Models; using DinkToPdf.Contracts; using DinkToPdf; namespace AthlosifyWebArchery { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { //services.AddHttpContextAccessor(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools())); services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); // Extended Application User from IdentityUser // and ApplicationRole from IdentityRole services.AddIdentity<ApplicationUser, ApplicationRole>( options => options.Stores.MaxLengthForKeys = 128) .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultUI() .AddDefaultTokenProviders(); services.AddMvc() .AddRazorPagesOptions(options => { options.Conventions.AuthorizeFolder("/Tournaments"); options.Conventions.AuthorizeFolder("/TournamentAtheletes"); options.Conventions.AuthorizeFolder("/TournamentBatches"); options.Conventions.AuthorizeFolder("/TournamentContingents"); options.Conventions.AuthorizeFolder("/Admin"); //options.Conventions.AuthorizeFolder("/Private"); //options.Conventions.AllowAnonymousToPage("/Private/PublicPage"); //options.Conventions.AllowAnonymousToFolder("/Private/PublicPages"); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ApplicationDbContext context, RoleManager<ApplicationRole> roleManager, UserManager<ApplicationUser> userManager) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseAuthentication(); app.UseMvc(); //UserManagerInitialData.Initialize(context, userManager, roleManager).Wait(); } } }
-
Celsius over 5 yearsCan elaborate more on this, please?
-
Celsius over 5 yearsIsn't that why we did?!?!
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor httpContextAccessor ) : base(options) { _userName = httpContextAccessor.HttpContext.User.Identity.Name; //_userID = userManager.GetUserId(httpContext.HttpContext.User); }
-
Celsius over 5 yearsDidn't think about that ... work well. Thanks. We slighly modifed to get the user id by:
var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);