skip to Main Content

I need to log certain user actions, such as Login, logout, CRUD, etc. and save to a table called AuditRecords. I have a DbContext called AuditContext:

I have learned that applying an ActionFilter and decorate a controllermethod with it is the way to go. The AuditContext is causing a red squiggly asking for the ‘options’, which baffles me. I have tried DI, but got the same results. Any thoughts? I am on DotNet 5.01, but I tried it withg DotNet 6 as well with the same results.

EDIT
I changed the AuditAttribute and Context to make it as straightforward as possible and by using Dependency Injection, as advised. But the problem is still not solved.

 public class Audit
{
    //Audit Properties
    public Guid AuditID { get; set; }
    public string UserName { get; set; }
    public string IPAddress { get; set; }
    public string AreaAccessed { get; set; }
    public DateTime Timestamp { get; set; }

    //Default Constructor
    public Audit() { }
}

public class AuditContext : DbContext
{
    public DbSet<Audit> AuditRecords { get; set; }
}
public class HomeController : Controller
{
   //red squiggly under [Audit] decoration with error of no formal
   //parameter 'context' in AuditAttribute.AuditAttribute(AuditContext)
    [Audit]  
    public IActionResult Privacy()
    {
        return View();
    }

//AuditAttribute class
using Microsoft.AspNetCore.Mvc.Filters;
using System;

namespace LoggingDemoMVC.Filters
{
public class AuditAttribute : ActionFilterAttribute
{
    private readonly AuditContext _context;

    public AuditAttribute(AuditContext context)
    { 
        _context = context;
    } 

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        //Stores the Request in an Accessible object
        var request = filterContext.HttpContext.Request;
        //Generate an audit
        Audit audit = new Audit()
        {
            //Your Audit Identifier     
            AuditID = Guid.NewGuid(),
            //Our Username (if available)
            UserName = (filterContext.HttpContext.User.Identity.IsAuthenticated) ? filterContext.HttpContext.User.Identity.Name : "Anonymous",
            //The IP Address of the Request
            IPAddress = request.Path,
            //The URL that was accessed
            AreaAccessed = request.HttpContext.Request.Path,
            //Creates our Timestamp
            Timestamp = DateTime.UtcNow
        };

        //Stores the Audit in the Database
        
        _context.AuditRecords.Add(audit);
        _context.SaveChanges();

        //Finishes executing the Action as normal 

        base.OnActionExecuting(filterContext);
    }
}

}

2

Answers


  1. WebAppContext expects a DbContext<WebAppContext> to be passed into its constructor . However , you are calling the constructor without providing anything :

    WebAppContext _context = new WebAppContext()
    

    To fix the issue , you can just plug into the Dependency Injection system that you’ve set up

    public class CustomFilter : IActionFilter
        {
            private readonly WebAppContext _context;
            public CustomFilter(WebAppContext context)
            {
                _context = context;
            }
          
            public void OnActionExecuting(ActionExecutingContext context)
            {
                //.......
                    
                _context.AuditRecords.Add(...);
                _context.SaveChanges();
            }
        }
    

    Simple Demo :

    model

    public class Audit
        {
            public int ID { get; set; }
            public string Name { get; set; }
        }
    

    DataContext

     public class WebAppContext : DbContext
        {
            public WebAppContext(DbContextOptions<WebAppContext> options) : base(options)
            {
    
            }
            public DbSet<Audit> AuditRecords { get; set; }
        }
    

    Custom Filter

    public class CustomFilter :Attribute,IActionFilter
        {
            private readonly WebAppContext _context;
            public CustomFilter(WebAppContext context)
    
            {
                _context = context;
            }
    
            public void OnActionExecuted(ActionExecutedContext context)
            {
                Console.WriteLine("end");
            }
    
            public void OnActionExecuting(ActionExecutingContext context)
            {
                Audit audit = new Audit();
               
                audit.Name = "mike";
                    
                _context.AuditRecords.Add(audit);
                _context.SaveChanges();
            }
        }
    

    Startup

    public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllersWithViews();
                //config database
                services.AddDbContext<WebAppContext>(option=>option.UseSqlServer(Configuration.GetConnectionString("default")));
                //add filter
                services.AddScoped<CustomFilter>();
            }
    

    controller

    [TypeFilter(typeof(CustomFilter))]
    public IActionResult Privacy()
    {
        return View();
    }
    

    when I access Privacy method , The database will add a row of data

    Login or Signup to reply.
  2. First, add the Nuget package Microsoft.EntityFrameworkCore.SqlServer to your project.
    Then, add the following code to the ConfigureServices method of your Startup class:

    services.AddDbContext<WebAppContext>(options =>
     options.UseSqlServer("[your connectionstring here]"));
    

    That’s the bare minimum and this will enable you to inject the DbContext into the ActionFilter.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search