Seifolah
Seifolah

Reputation: 551

asp.net mvc web api 2 custom logger + controller lifecycle

story:I have following component in my asp.net mvc 5 web api project(using ef code first)

  1. myBaseController : base api controller class
  2. myLogger : my costume logger class

    class myBaseController { MyLogger Logger; }

    class MyLogger { public addLog(Log log) { db.logs.add(log); db.saveChanges(); } }

Each api controller use base.logger.addLog(new log("somethings")) and the log committed to database immediately.

Problem: I want MyLogger class be able to collect all log in memory and some where add theme to database at once. I put it in

protected override void Dispose(bool disposing) { logger.AddRange(_logs); }

but it doesn't work,it seems objects are not available in this method or grabbed from memory

How it should be handled?

Upvotes: 0

Views: 619

Answers (3)

VahidN
VahidN

Reputation: 19156

You don't need to collect records in memory separately. Because EF will do that for you. For instance db.logs.add means, please add it to memory. But when you call SaveChanges it will commit all of the in-memory collected data to the database at once. So you will need to implement the unit of work pattern (UoW). By using this pattern, you will have only one context per request and then at the end of the request you will call the SaveChanges method once. You shouldn't have multiple SaveChanges everywhere in your codes, this is your problem right now. Your addLog method calls SaveChanges, your other methods call SaveChanges and so on.

More info from Microsoft: Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application

Upvotes: 3

Win
Win

Reputation: 62260

I normally create ActionFilterAttributes, and place it on action methods where I want to log any activity.

TraceMvcAttribute

public class TraceMvcAttribute : ActionFilterAttribute
{
   // IoC container will inject this dependencies.
   public IWebUserSession WebUserSession { get; set; }
   public IDateTime DateTime { get; set; }
   public ITraceListener TraceListener { get; set; }

   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
      var traceLog = new TraceLog
      {
         Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
         Action = filterContext.ActionDescriptor.ActionName,
         Message = JsonConvert.SerializeObject(filterContext.ActionParameters),
         PerformedOn = this.DateTime.Now,
         PerformedBy = WebUserSession?.UserName
      };
      TraceListener.AddTraceLogAsync(traceLog);
      base.OnActionExecuting(filterContext);
   }
}

TraceApiAttribute

Web API filter is a little bit different from MVC filter. I do not have it in my sample code at Git; in case you might want to take a look at it.

public class TraceApiAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        // http://autofac.readthedocs.org/en/latest/integration/webapi.html#standard-web-api-filters-are-singletons
        // All filter attributes in Web API are effectively singleton instances that exist 
        // for the entire lifetime of the application. We must use service location, 
        // since we need per-request services in your filters.
        var requestScope = actionContext.Request.GetDependencyScope();
        var datetime = requestScope.GetService(typeof(IDateTime)) as IDateTime;
        var webUserSession = requestScope.GetService(typeof(IWebUserSession)) as IWebUserSession;
        var traceListener = requestScope.GetService(typeof(ITraceListener)) as ITraceListener;

        var traceLog = new TraceLog
        {
            Controller = actionContext.ControllerContext.ControllerDescriptor.ControllerName,
            Action = actionContext.ActionDescriptor.ActionName,
            Message = JsonConvert.SerializeObject(actionContext.ActionArguments.Where(x => x.Key != "request").ToList()),
            PerformDateTime = datetime.Now,
            PerformBy = webUserSession?.UserName
        };
        traceListener.AddTraceLogAsync(traceLog);
        base.OnActionExecuting(actionContext);
    }
}

I then view the log history like this -

enter image description here

Upvotes: 0

Jignesh Variya
Jignesh Variya

Reputation: 1919

You need to store your entries in memory during request cycle and at end of request you can save those entries into database

 protected void Application_EndRequest()
    {
      //get entries from memory/session/cache whatever your source to store entries
      //do stuff to save entries
    }

Upvotes: 0

Related Questions