Search code examples
ef-database-firstjson-apiaudit-logging

ChangeTracker implementation with database first appraoch


Our company is new to using EF and I would appreciate any guidance/advise on our problem. In this project we are using EF 6, Visual Studio 2015, a database first approach and RESTful service(this is a RESTful API using JSON not a generic API, we only need the audit logging method to be generic). We need to implement auditing and are investigating using the ChangeTracker functionality. The idea is to make a generic method that can accept any entity. Here's what we have so far;

End of Shift API controller:

public class EndOfShiftsController : ApiController
{
    private iSuiteEntities db = new iSuiteEntities();    
    // POST: api/EndOfShifts/post
    [Route("api/EndOfShift/post")]
    [ResponseType(typeof(EndOfShift))]
    public async Task<IHttpActionResult> PostEndOfShift_Post(EndOfShiftDto endOfShift)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        EndOfShift endofshift = new EndOfShift()
        {
            EndOfShiftID = endOfShift.EndOfShiftID,
            EndOfShiftDate = endOfShift.EndOfShiftDate,
            EquipmentID = endOfShift.EquipmentID,
            StartHours = endOfShift.StartHours,
            EndHours = endOfShift.EndHours,
            CreatedBy = endOfShift.CreatedBy,
            CreateDate = DateTime.Now,
            Active = true
        };

        try
        {
            if (!endOfShift.ToDelete)
            {
                if (EndOfShiftExists(endOfShift.EndOfShiftID))
                {
                    var update = db.EndOfShifts.Find(endOfShift.EndOfShiftID);
                    update.EndOfShiftDate = endofshift.EndOfShiftDate;
                    update.EquipmentID = endofshift.EquipmentID;
                    update.StartHours = endOfShift.StartHours;
                    update.EndHours = endOfShift.EndHours;
                    db.Entry(update).State = EntityState.Modified;

 //try calling the audit log method here/////////////////////////////////
                    AuditsController.GetAuditLogData(endofshift, endOfShift.EndOfShiftID, endOfShift.CreatedBy);
                    await db.SaveChangesAsync();
                }
                else
                {
                    db.EndOfShifts.Add(endofshift);
                    await db.SaveChangesAsync();
                }
            }
            else
            {
                EndOfShift delete = db.EndOfShifts.Find(endOfShift.EndOfShiftID);
                if (delete == null)
                {
                    return NotFound();
                }
                else
                {
                    delete.Active = false;
                    db.Entry(delete).State = EntityState.Modified;
                    await db.SaveChangesAsync();
                }
            }



            return Ok("Success");
        }   
        catch(Exception ex)
        {
            return Ok(ex.Message);
        }            
        //return CreatedAtRoute("DefaultApi", new { id = endOfShift.EndOfShiftID }, endOfShift);
    }

Generic audit logging method something like this:

public class AuditsController : ApiController
{
    private static iSuiteEntities dbContext = new iSuiteEntities();
    private static iSuiteEntities db = new iSuiteEntities();

    //write Audit log        
    public static async void GetAuditLogData<T>(T entity, int recID, int modByID) where T : new ()
    {
        try
        {                
            var changeTrack = dbContext.ChangeTracker.Entries().Where(p => p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified);
            foreach (var entry in changeTrack)
            {
                if (entry.Entity != null)
                {
                    string entityName = string.Empty;
                    string state = string.Empty;
                    switch (entry.State)
                    {
                        case EntityState.Modified:
                            entityName = ObjectContext.GetObjectType(entry.Entity.GetType()).Name;
                            state = entry.State.ToString();
                            foreach (string prop in entry.OriginalValues.PropertyNames)
                            {
                                object currentValue = entry.CurrentValues[prop];
                                object originalValue = entry.OriginalValues[prop];
                                if (!currentValue.Equals(originalValue))
                                {
                                    Audit auditEntry = new Audit()
                                    {
                                        recordID = recID,
                                        tableName = entityName,
                                        fieldName = prop,
                                        oldValue = Convert.ToString(originalValue),
                                        editReason = "Update",
                                        modifiedBy = modByID,
                                        modifiedDate = DateTime.Now
                                    };

                                    db.Audits.Add(auditEntry);
                                    await db.SaveChangesAsync();
                                }
                            }
                            break;
                            //data addition is not required to be logged/////                            
                        case EntityState.Deleted:
                            entityName = ObjectContext.GetObjectType(entry.Entity.GetType()).Name;
                            state = entry.State.ToString();
                            foreach (string prop in entry.OriginalValues.PropertyNames)
                            {
                                Audit auditEntry = new Audit()
                                {
                                    recordID = recID,
                                    tableName = entityName,
                                    fieldName = prop,
                                    oldValue = Convert.ToString(entry.OriginalValues[prop]),
                                    editReason = "Delete",
                                    modifiedBy = modByID,
                                    modifiedDate = DateTime.Now
                                };

                                db.Audits.Add(auditEntry);
                                await db.SaveChangesAsync();                                    
                            }
                            break;
                        default:
                            break;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            //handle exception here....
        }
    }

Is this a feasible option for us to pursue for our audit logging requirement? The code has not been tested, so I'm sure we need some pointers coding wise too. If this is not a viable option to achieve our goal, can you please recommend an option that can be implemented with the database first approach? Thanks in advance!!


Solution

  • After taking a different approach to what we were Googling, we came upon this link which gave us the solution to our problem. Leaving this here to hopefully save other developers some time :-)