.NET 5 - jak wstrzknąć IHttpContextAccessor do DbContext zarejestrowany jak DbContextPool

0

Chciałbym dodawać dane audytowe podczas modyfikacji i zapisu z poziomiu DbContextu.
Idzie to w ogóle zrobić przy "poolowaniu" kontekstów ?
Jakieś podpowiedzi jak to zrobić?

0

Hmm ciekawe. Może wykorzystać by wzorzec decorator?

0

@szydlak: Tylko jak to później zarejestrować ?

0

Nie wiem czy dobrze rozumiem. Ja to mam tak w jednym z projektów:

Startup.cs

services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<UserResolverService>();

UserResolverService.cs

public class UserResolverService
    {
        private readonly IHttpContextAccessor _context;

        public UserResolverService(IHttpContextAccessor context)
        {
            _context = context;
        }

        public int GetUserId()
        {
            // Ja wrzucałem dane user do Claims'ów
            ClaimsPrincipal cp = _context.HttpContext.User;

            ClaimsIdentity ci = cp.Identities.FirstOrDefault();
            string userJson = ci.Claims.FirstOrDefault(x => x.Type == "user").Value;

            if(!string.IsNullOrEmpty(userJson))
            {
                AppUser user = JsonConvert.DeserializeObject<AppUser>(userJson);
                return user.Id;
            }

            return 0;
        }
    }

KasiarzContext.cs

    public class KasiarzContext : DbContext
    {
        private readonly UserResolverService _userResolver;

        public KasiarzContext(DbContextOptions<KasiarzContext> options, UserResolverService userResolver)
            : base(options)
        {
            _userResolver = userResolver;
        }

        public override int SaveChanges()
        {
            OnBeforeSaving();
            return base.SaveChanges();
        }

        public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)) 
        {
            OnBeforeSaving();
            return base.SaveChangesAsync();
        }

        private void OnBeforeSaving()
        {
            var entries = ChangeTracker.Entries();
            foreach(var entry in entries)
            {
                if(entry.Entity is IDbTracking tracking)
                {
                    var now = DateTime.Now;
                    //var user = 0;
                    var user = _userResolver.GetUserId();

                    switch (entry.State)
                    {
                        case EntityState.Modified:
                            tracking.ModifiedAt = now;
                            tracking.ModifiedBy = user;
                            break;
                        case EntityState.Added:
                            tracking.CreatedAt = now;
                            tracking.CreatedBy = user;
                            tracking.ModifiedAt = now;
                            tracking.ModifiedBy = user;
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    }

To jest stary projekt. Tam są dopisywane tylko rzeczy jak trzeba. Nigdy nie było to "dopracowywane" do .NET 5, a pisane chyba było w .NET Core 2.1. Może coś nakieruje ;-)

0

@AdamWox: mam to bardzo podobnie zrobione. Jednak jak zarejestrujesz DbContext jako DbContextPool to wtedy nie otrzymasz kontekstu użytkownika wstrzykując IHttpContextAccessor.

1

Robisz tak: W twoim dbcontext wystawiasz metode na ustawienie IHttpContextAccessor + jakieś pole na to.
Robisz klase Np DbContextProvider zarejestrowana jako Scoped. Tak klasa Ma wstrzyknięty dbcontext zarejestrowany jako pool. DbContextProvider przed zwróceniem ustawia odpowiedni IHttpContextAccessor w Dbcontext. Reszte jak u @AdamWox. To tak na szybko jakbym miał coś kombinować.

1 użytkowników online, w tym zalogowanych: 0, gości: 1