Audit.NET icon indicating copy to clipboard operation
Audit.NET copied to clipboard

Not available to use dbContext in .UseDbContext<MyDbContext> in Audit.Core.Configuration.Setup

Open Arce19 opened this issue 2 years ago • 3 comments

Im not available to use UseDbContext<MyDbContext> in my UseEntityFramework configuration, im getting this error 'Constructor on type 'DbContext' not found.', i added that line because i had the loop problem, it work but getting the service of my applicationDbContext like this 'return services.BuildServiceProvider().GetService<ApplicationDbContext>()', but i think thats not the right way, i already added the [AuditIgnore] to my Auidtable entity, ill leave my snippets of code below

this is my startup config

public class Startup
  {
       public IConfiguration Configuration { get; }
       public Startup(IConfiguration configuration)
       {
           Configuration = configuration;
       }

       public virtual void ConfigureServices(IServiceCollection services)
       {
           // Configure services
           services.AddDbContext<ApplicationDbContext>(c => c
               .UseSqlServer(Configuration.GetConnectionString("EntityFrameworkConnection"))
               .AddInterceptors(new AuditSaveChangesInterceptor()));
             
             Audit.Core.Configuration.Setup()
                 .UseEntityFramework(_ => _
                    .UseDbContext<ApplicationDbContext>()
                    .AuditTypeMapper(t => typeof(Auditable))
                    .AuditEntityAction<Auditable>((ev, entry, entity) =>
                    {
                       entity.Comments = "";
                       entity.Action = entry.Action;
                       entity.Ip = "";
                       entity.UserName = ev.Environment.UserName;
                       entity.ActionDate = DateTimeOffset.UtcNow;
                       entity.Longitude = 0;
                       entity.Latitude = 0;
                       entity.Browser = "";
                       entity.Success = ev.GetEntityFrameworkEvent().Success;
                    })
                .IgnoreMatchedProperties(true));
           }
 }

im getting this error Constructor on type 'DbContext' not found.

this is my db context

public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
        public DbSet<Auditable> Auditable { get; set; }
        public DbSet<CurrencyPair> CurrencyPair { get; set; }

    }

if i do tihis:

.UseDbContext(ev =>
    {
        return services.BuildServiceProvider().GetService<ApplicationDbContext>();
    })

instead of: .UseDbContext<ApplicationDbContext>() works but i dont thinks is the right way

Arce19 avatar Feb 15 '24 17:02 Arce19

Yes, that's one way to do it.

It may seem incorrect because you are using the service provider in the method that should be configuring the service provider...

I think it would be better to move the Audit.Core.Configuration.Setup() call to the Startup.Configure method.

For example:

public void Configure(IApplicationBuilder app)
{
	Audit.Core.Configuration.Setup()
    	.UseEntityFramework(x => x
        	.UseDbContext(ev => app.ApplicationServices.GetService<ApplicationDbContext>()));
}

Check this SO answer: https://stackoverflow.com/a/32461714/122195

thepirat000 avatar Feb 15 '24 18:02 thepirat000

i tried that moving the Audit.Core.Configuration.Setup() to the Startup.Configure method, but now im getting this error Cannot resolve scoped service 'Demo.Data.ApplicationDbContext' from root provider., using this .UseDbContext(ev => app.ApplicationServices.GetService<ApplicationDbContext>())

Arce19 avatar Feb 15 '24 19:02 Arce19

Please check this: https://github.com/thepirat000/Audit.NET/issues/539

Maybe this should work for you:

Audit.Core.Configuration.Setup()
    .UseEntityFramework(ef => ef
        .UseDbContext(ev => app.ApplicationServices.CreateScope().ServiceProvider.GetRequiredService<ApplicationDbContext>())
        .DisposeDbContext()
        ....);

thepirat000 avatar Feb 16 '24 00:02 thepirat000

Thanks, it worked, by created a new scope im getting a new instance of DbContext that works well, but im having a little trouble, when i try to get a registered scope service im getting a new instance of that service, and i would like to get the current scope service in a request

Startup()
  services.AddScoped<IRequestContext, RequestContext>();

Configure(IApplicationBuilder app)
  var serviceProvider = app.ApplicationServices.CreateScope().ServiceProvider;
  Audit.Core.Configuration.Setup()
              .UseEntityFramework(_ => _
                  .UseDbContext(ev =>
                  {
                      return serviceProvider.GetRequiredService<ApplicationDbContext>();
                  })
                  .AuditTypeMapper(t => typeof(Auditable))
                  .AuditEntityAction<Auditable>((ev, entry, entity) =>
                  {
                      **var requestContext = serviceProvider.GetRequiredService<IRequestContext>();**   //<--  This line
                  })
              .IgnoreMatchedProperties(true));

i would like to know if there is a way to use DI to get that current scoped service in a request, or if i can apply something in a AddCustomAction method, im trying to fill some fields with user data that i got in that requestContext.

Arce19 avatar Feb 23 '24 16:02 Arce19

Yes, you can !

Since you can access the DbContext from the event, and the DbContext provides access to service provider:

        .AuditEntityAction<Auditable>((ev, entry, entity) =>
        {
            var requestContext = ev.GetEntityFrameworkEvent().GetDbContext().GetService<IRequestContext>(); 
        })

thepirat000 avatar Feb 24 '24 02:02 thepirat000

Works perfect!, thank you for the detailed explanation and your answers @thepirat000

Arce19 avatar Feb 24 '24 03:02 Arce19