Возврат пустого значения на httpContextAccessor.HttpContext - PullRequest
0 голосов
/ 23 ноября 2018

Мы переопределяем SaveChangesAsync () для автоматического обновления для DateCreated, CreatedBy, LastDateModified и LastModifiedBy.С CreatedBy и LastModifiedBt нам нужен идентификатор пользователя.

В нашем конструкторе для ApplicationDbContext мы добавили что-то вроде этого:

_userName = httpContextAccessor.HttpContext.User.Identity.Name; //_userID = userManager.GetUserId(httpContext.HttpContext.User);

.и всегда получайте значение NULL в этом httpContextAccessor.HttpContext.Есть идеи?Мы включили источник ниже.

Среда:

.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();

        }


    }
}

1 Ответ

0 голосов
/ 23 ноября 2018

HttpContext действительно только во время запроса.Когда .NET Core создает класс ApplicationDbContext для вызова Configure, допустимый контекст отсутствует.

Вам необходимо сохранить ссылку на IHttpContextAccessor в конструкторе DbContext, а затем вы можетеиспользуйте эту переменную для доступа к свойству HttpContext в вашем методе OnBeforeSaving().

Например:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
    private readonly IHttpContextAccessor _httpContextAccessor;


    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options,
            IHttpContextAccessor httpContextAccessor
            )
    : base(options)
    {
        _httpContextAccessor = httpContextAccessor;
    }

   ....

}

Затем в вашем методе OnBeforeSaving ():

private void OnBeforeSaving()
{
    var userName = _httpContextAccessor.HttpContext.User.Identity.Name;

    ...
}

Думайте о HttpContext как о телефонном звонке.Если вы берете трубку, когда никто не звонил, значит, context нет, т. Е. Он нулевой.Когда кто-то звонит, у вас есть действительный context.Это тот же принцип для веб-звонка.Метод Configure в автозагрузке не является веб-вызовом и поэтому не имеет HttpContext.

с другого сайта:

Объект HttpContext будет содержать информацию отекущий HTTP-запрос.Подробно, объект HttpContext будет создаваться заново для каждого запроса, переданного приложению ASP.Net, и этот объект будет содержать информацию о текущем запросе, такую ​​как запрос, ответ, сервер, сессия, кэш, пользователь и т. Д. Для каждого запроса новый HttpContextБудет создан объект, который среда выполнения ASP.Net будет использовать при обработке запроса.Новый объект HttpContext будет создан в начале запроса и уничтожен после его завершения.

...