Save Activity Log in ASP.NET Core Web API like a Pro

In Any Web Application activity logs are the most important and common thing. Today we will learn how can we save activity log in ASP.NET Core Web API project. There are many ways to perform this task but we are going to discuss one of the efficient ways with you.

We will keep record of activity logs by overriding our savechanges() and savechangesasync() method.

Tools:

For this project, I’m using Visual studio community 2019, windows os, and .NET Core Version 5.

Let’s Start:

To save logs we have to create a table in our database. Create a class and name it “Activitylog.cs”.

public class ActivityLog 
    {
        public Guid Id { get; set; }
        public Guid? UserId { get; set; }
        public DateTime? Date { get; set; }
        public string Operation { get; set; }
        public string TableName { get; set; }
        public string EntityId { get; set; }
        public string EntityDataJson { get; set; }
    }

In this class we have multiple properties like UserId for who performed the action, Date when performed the action, Opertion what the operation was, TableName in which table, EntityId on which record action has been performed, and EntityDataJson to save all operation Data.

Now we have to override our SaveChangesAsync() and SaveChanges() method. So whenever any operation will be performed on database and we call SaveChanges() or SaveChangesAsync() to save the changes into our database the Activity log will be automatically saved.

Lets open our “ApplicationDbContext.cs” class.  And create a private method that will save all activity logs into the database and we will call this function into both SaveChanges() or SaveChangesAsync() functions.

private void OnBeforeSaving()
        {
            foreach (var entry in ChangeTracker.Entries().ToList())
            {
                if (entry.Entity.GetType().Name != "IdentityUserToken`1" 
                    && entry.Entity.GetType().Name != "SessionLog" 
                    && entry.Entity.GetType().Name != "EmailTemplate" 
                    && entry.Entity.GetType().Name != "EmailPlaceholder"
                    && entry.Entity.GetType().Name != "ApplicationRole" 
                    && entry.Entity.GetType().Name != "ExceptionLog"
                    && entry.Entity.GetType().Name != "IdentityUserRole`1"
                    && entry.Entity.GetType().Name != "Module")
                {
                    if (entry.State == EntityState.Added)
                    {
                        var log = new ActivityLog();
                        log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                        {
                            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                        });
                        Guid userid;
                        var usercheck = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
                        if (!string.IsNullOrEmpty(usercheck))
                            userid = Guid.Parse(usercheck);
                        else
                            userid = new Guid();

                        if (userid != Guid.Empty)
                            log.UserId = userid;
                        log.EntityId = entry.CurrentValues["Id"].ToString();
                        log.Operation = "Add";
                        log.Date = DateTime.Now;
                        log.TableName = entry.Entity.GetType().Name;
                        this.ActivityLogs.Add(log);
                    }
                    else if (entry.State == EntityState.Modified)
                    {
                        if(entry.Entity.GetType().Name == "ApplicationUser" ||entry.Entity.GetType().Name == "UserInformation" || entry.Entity.GetType().Name == "State"|| entry.Entity.GetType().Name == "County"|| entry.Entity.GetType().Name == "City")
                        {
                            var log = new ActivityLog();
                            log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                            {
                                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                            });
                            Guid userid;
                            var usercheck = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
                            if (!string.IsNullOrEmpty(usercheck))
                                userid = Guid.Parse(usercheck);
                            else
                                userid = new Guid();

                            if (userid != Guid.Empty)
                                log.UserId = userid;

                            log.EntityId = entry.CurrentValues["Id"].ToString();
                            log.Operation = "Update";
                            log.Date = DateTime.Now;
                            log.TableName = entry.Entity.GetType().Name;
                            this.ActivityLogs.Add(log);
                        }
                        else
                        {
                            var deleteValue = entry.CurrentValues["Active"].ToString();
                            if (deleteValue == "False" || deleteValue == "false")
                            {
                                var log = new ActivityLog();
                                log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                                {
                                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                                });
                                var userid = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
                                    //userid = new Guid(entry.Property("UserId").CurrentValue.ToString());
                                if (!string.IsNullOrEmpty(userid))
                                    log.UserId = Guid.Parse(userid);

                                log.EntityId = entry.CurrentValues["Id"].ToString();
                                log.Operation = "Delete";
                                log.Date = DateTime.Now;
                                log.TableName = entry.Entity.GetType().Name;
                                this.ActivityLogs.Add(log);
                            }
                            else
                            {
                                var log = new ActivityLog();
                                log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                                {
                                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                                });
                                var userid = Guid.Parse(_httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
                                //var userid = new Guid(entry.Property("UserId").CurrentValue.ToString());
                                if (userid != Guid.Empty)
                                    log.UserId = userid;
                                log.EntityId = entry.CurrentValues["Id"].ToString();
                                log.Operation = "Update";
                                log.Date = DateTime.Now;
                                log.TableName = entry.Entity.GetType().Name;
                                this.ActivityLogs.Add(log);
                            }
                        }
                    }
                    else if (entry.State == EntityState.Deleted)
                    {
                        var log = new ActivityLog();
                        log.EntityDataJson = JsonConvert.SerializeObject(entry.Entity, new JsonSerializerSettings
                        {
                            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                        });
                        var userid = 
                      Guid.Parse(_httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
                        log.UserId = userid;

                        if (!string.IsNullOrEmpty(entry.CurrentValues["Id"].ToString()))
                            log.EntityId = entry.CurrentValues["Id"].ToString();
                        log.Operation = "Delete";
                        log.Date = DateTime.Now;
                        log.TableName = entry.Entity.GetType().Name;
                        this.ActivityLogs.Add(log);
                    }
                }  
            }
        }

In this function, we are iterating on the change tracker class this class in the entity framework has all the information about changes that we have made with entities. After iterating this we will save the changes into the Activity logs.

Now, let’s call the function in the methods.

public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
            CancellationToken cancellationToken = new CancellationToken())
        {
            OnBeforeSaving();
            return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
        }
        public override int SaveChanges(bool acceptAllChangesOnSuccess)
        {
            OnBeforeSaving();
            return base.SaveChanges(acceptAllChangesOnSuccess);
        }

After calling the private method whenever the save changes are applied after any entity manipulation. Activity will be saved with the user record. This is one of the optimized and professional approaches to do this.

Conclusion:

There are many approaches to do this. But this is the most optimized and professional approach to do this task. Try this tutorial and if you do not understand anything or face any issue while applying this method. Do not hesitate to comment below. MYCODEBIT team will try to respond ASAP.

Leave a Reply

Your email address will not be published.