Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance audit logging for foreign key changes to include related entity fields #721

Open
vivek-outamation opened this issue Jan 16, 2025 · 1 comment

Comments

@vivek-outamation
Copy link

Description

In the current implementation, when a foreign key value (e.g., CourseId in the Student entity) is updated, the audit log stores the foreign key value (CourseId) in the Changes column of the audit log. However, the requirement is to capture a meaningful field from the related entity (CourseName from the Course entity) instead of the foreign key value.

Steps to Reproduce

  • Update the CourseId in the Student entity.
  • Observe that the audit log captures the CourseId in the Changes column.
  • The expected behavior is to capture CourseName instead of CourseId.

Expected Behavior

When the CourseId is updated in the Student entity:
The audit log should include the CourseName (retrieved from the Course entity) in the Changes column.

My Code Snippet

using System.ComponentModel.DataAnnotations.Schema;

namespace AuditTest.Entity
{
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public int CourseId { get; set; }
        [ForeignKey("CourseId")]
        [InverseProperty("Students")]
        public virtual Course Course { get; set; }
    }
}
using System.ComponentModel.DataAnnotations.Schema;

namespace AuditTest.Entity
{
    public class Course
    {
        public int Id { get; set; }
        public string CourseName { get; set; }
        public string Description { get; set; }
        [InverseProperty("Course")]
        public virtual ICollection<Student> Students { get; set; }
    }
}

using Audit.EntityFramework;
using System.ComponentModel.DataAnnotations;

namespace AuditTest.Entity
{
    [AuditIgnore]
    public class AuditLog
    {
        [Key]
        public int AuditId { get; set; }
        public string TableName { get; set; }
        public List<AuditChanges> Changes { get; set; }
        public int PrimaryKey { get; set; }
        public DateTime ModifiedOn { get; set; }
        public int ModifiedById { get; set; }
    }

    public class AuditChanges
    {
        public string ColumnName { get; set; }
        public object OriginalValue { get; set; }
        public object NewValue { get; set; }
    }
}

Audit Configuration

Audit.Core.Configuration.Setup()
    .UseEntityFramework(ef => ef
    .AuditTypeExplicitMapper(m => m
    .Map<Student>(m => m.Action == "Update"? typeof(AuditLog) : null)
    .Map<Course>(m => m.Action == "Update" ? typeof(AuditLog) : null)
    .AuditEntityAction<AuditLog>((ev, entry, auditLog) =>
    {
	auditLog.TableName = entry.Table;
	auditLog.ModifiedOn = DateTime.Now;
	auditLog.ModifiedById = 1;
	auditLog.PrimaryKey = (int)entry.PrimaryKey.First().Value;
	auditLog.Changes = entry.Changes.Select(ch => {
		new AuditChanges
            {
                ColumnName = ch.ColumnName,
                OriginalValue = ch.OriginalValue,
                NewValue = ch.NewValue
            };
            }).ToList();
	})));
  • Is there any way so I can configure that whenever courseId changes in student table changes include courseName instead of courseId?
@thepirat000
Copy link
Owner

thepirat000 commented Jan 18, 2025

You can include custom logic to retrieve the Course Name during the audit entity action.

Assuming you are auditing only the Student entity and the Course information is already loaded in the entity, you could implement it like this:

Audit.Core.Configuration.Setup()
    .UseEntityFramework(ef => ef
        .AuditTypeExplicitMapper(m => m
            .Map<Student>(m => m.Action == "Update" ? typeof(AuditLog) : null)
            .AuditEntityAction<AuditLog>((ev, entry, auditLog) =>
            {
                // ...
                auditLog.Changes = entry.Changes.ConvertAll(ch => new AuditChanges()
                {
                    ColumnName = ch.ColumnName,
                    OriginalValue = ch.OriginalValue,
                    NewValue = ch.NewValue
                });

                // Add the CourseName to the changes collection
                auditLog.Changes.Add(new AuditChanges()
                {
                    ColumnName = "CourseName",
                    NewValue = (entry.GetEntry().Entity as Student)?.Course?.CourseName
                });

If you also require the old value of the course name, you may need to query the database.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants