Как получить текущий зарегистрированный идентификатор пользователя в ApplicationDbContext с помощью Identity? - PullRequest
0 голосов
/ 20 октября 2018

Я создал приложение .net core 2.1 MVC, используя шаблон в Visual Studio с предустановкой Identity (учетные записи пользователей хранятся в приложении), и я пытаюсь автоматизировать некоторые поля аудита.

В основном то, что яя пытаюсь переопределить метод SaveChangesAsync (), чтобы при внесении изменений в сущность текущий зарегистрированный идентификатор пользователя устанавливался в свойство аудита свойств CreatedBy или ModifiedBy, которые создаются как теневые свойства объекта.

Я посмотрел на то, что кажется тоннами ответов, и удивительно, что ни один из них не работает для меня.Я попытался внедрить IHttpContext, HttpContext, UserManager, и я либо не могу получить доступ к методу, который возвращает идентификатор пользователя, либо я получаю циклическую ошибку зависимости, которая я не совсем понимаю, почему это происходит.

Я действительно бегу в отчаянии с этим.Я думаю, что-то вроде этого должно быть действительно простым делом, но мне очень трудно понять, как это сделать.Кажется, есть хорошо документированные решения для контроллеров веб-API или контроллеров MVC, но не для использования внутри ApplicationDbContext.

Если кто-то может мне помочь или, по крайней мере, указать мне правильное направление, я был бы очень благодарен,спасибо.

Ответы [ 2 ]

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

Спасибо за все ответы.В конце концов я решил создать UserResolveService, который получает через DI HttpContextAccessor и может затем получить имя текущего пользователя.С именем я могу затем запросить базу данных, чтобы получить любую информацию, которая мне может понадобиться.Затем я внедряю этот сервис в ApplicationDbContext.

IUserResolveService.cs

public interface IUserResolveService
{
    Task<string> GetCurrentSessionUserId(IdentityDbContext dbContext);
}

UserResolveService.cs

public class UserResolveService : IUserResolveService
{
    private readonly IHttpContextAccessor httpContextAccessor;

    public UserResolveService(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public async Task<string> GetCurrentSessionUserId(IdentityDbContext dbContext)
    {
        var currentSessionUserEmail = httpContextAccessor.HttpContext.User.Identity.Name;

        var user = await dbContext.Users                
            .SingleAsync(u => u.Email.Equals(currentSessionUserEmail));
        return user.Id;
    }
}

Вы должны зарегистрировать сервис при запуске и внедрить егов ApplicationDbContext, и вы можете использовать его следующим образом:

ApplicationDbContext.cs

var dbContext = this;

var currentSessionUserId = await userResolveService.GetCurrentSessionUserId(dbContext);
0 голосов
/ 20 октября 2018

Давайте назовем это DbContextWithUserAuditing

public class DBContextWithUserAuditing : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
    public string UserId { get; set; }
    public int? TenantId { get; set; }

    public DBContextWithUserAuditing(DbContextOptions<DBContextWithUserAuditing> options) : base(options) { }

    // here we declare our db sets

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.NamesToSnakeCase(); // PostgreSQL
        modelBuilder.EnableSoftDelete();
    }

    public override int SaveChanges()
    {
        ChangeTracker.DetectChanges();
        ChangeTracker.ProcessModification(UserId);
        ChangeTracker.ProcessDeletion(UserId);
        ChangeTracker.ProcessCreation(UserId, TenantId);

        return base.SaveChanges();
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        ChangeTracker.DetectChanges();
        ChangeTracker.ProcessModification(UserId);
        ChangeTracker.ProcessDeletion(UserId);
        ChangeTracker.ProcessCreation(UserId, TenantId);

        return (await base.SaveChangesAsync(true, cancellationToken));
    }
}

Тогда у вас есть конвейер запроса и все, что вам нужно - это фильтр-хук , где вы устанавливаете свой UserID

public class AppInitializerFilter : IAsyncActionFilter
{
    private DBContextWithUserAuditing _dbContext;

    public AppInitializerFilter(
        DBContextWithUserAuditing dbContext
        )
    {
        _dbContext = dbContext;
    }

    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next
        )
    {
        string userId = null;
        int? tenantId = null;

        var claimsIdentity = (ClaimsIdentity)context.HttpContext.User.Identity;

        var userIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
        if (userIdClaim != null)
        {
            userId = userIdClaim.Value;
        }

        var tenantIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == CustomClaims.TenantId);
        if (tenantIdClaim != null)
        {
            tenantId = !string.IsNullOrEmpty(tenantIdClaim.Value) ? int.Parse(tenantIdClaim.Value) : (int?)null;
        }

        _dbContext.UserId = userId;
        _dbContext.TenantId = tenantId;

        var resultContext = await next();
    }
}

Этот фильтр активируется следующим образом (файл Startup.cs)

services
    .AddMvc(options =>
    {
        options.Filters.Add(typeof(OnRequestInit));
    })

После этого ваше приложение может автоматически устанавливать UserID и TenantID для вновь создаваемых записей

public static class ChangeTrackerExtensions
{
    public static void ProcessCreation(this ChangeTracker changeTracker, string userId, int? tenantId)
    {
        foreach (var item in changeTracker.Entries<IHasCreationTime>().Where(e => e.State == EntityState.Added))
        {
            item.Entity.CreationTime = DateTime.Now;
        }

        foreach (var item in changeTracker.Entries<IHasCreatorUserId>().Where(e => e.State == EntityState.Added))
        {
            item.Entity.CreatorUserId = userId;
        }

        foreach (var item in changeTracker.Entries<IMustHaveTenant>().Where(e => e.State == EntityState.Added))
        {
            if (tenantId.HasValue)
            {
                item.Entity.TenantId = tenantId.Value;
            }
        }
    }

Я бы не рекомендовал вводить HttpContext, UserManager или что-либо еще в ваш класс DbContext, потому что таким образом вы нарушаете Принцип единой ответственности .

...