Dailycode.info

Short solution for short problems

Audit trail (History) using the entity framework and self-tracking entities (Part2)

The reason why I’m writing part 2 is because I bumped into the problem that when you are using identity insert on the database, the identity is not known the moment the ObjectContext.Savingchanges event is fired.  This only gave problems for the insert statement. The identity key column was always 0. So I found a solution for this. I combined an audit project and a repository. For the update and delete, nothing really changed, but the way I handle the auditing for the insert is changed completely.

First of all, I removed the added objects from the GetObjectStateEntries call:

 

void Ents_SavingChanges(object sender, EventArgs e)

{

    try

    {

        Ents.DetectChanges();

        IEnumerable<ObjectStateEntry> changes = Ents.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted | EntityState.Modified);

        foreach (ObjectStateEntry stateEntryEntity in changes)

        {

            if (!stateEntryEntity.IsRelationship && stateEntryEntity.Entity != null && !(stateEntryEntity.Entity is GP_PROJ_DBAudit))

            {

                //is a normal entry, not a relationship

                GP_PROJ_DBAudit audit = this.AuditTrailFactory(stateEntryEntity, UserName);

                auditTrailList.Add(audit);

            }

        }

 

        if (auditTrailList.Count > 0)

        {

            foreach (var audit in auditTrailList)

            {//add all audits

                Ents.GP_PROJ_DBAudit.AddObject(audit);

            }

        }

        auditTrailList = new List<GP_PROJ_DBAudit>();

    }

    catch (Exception er)

    {

        //Audit falied, need to log it.

    }

}

 

Second, I created a function that is used for all inserts:

 

/// <summary>

/// Add entity to the repository

/// </summary>

/// <typeparam name="E">An Entity type object type which is going to be added</typeparam>

/// <param name="entity">The entity to be added</param>

/// <returns>True/False</returns>

/// <remarks></remarks>

private bool AddRecord<E>(E entity)

{

    if (entity is IObjectWithChangeTracker)

    {

        Ents.AddObject((typeof(E)).Name.ToString(), entity);

        ObjectStateEntry entry = Ents.ObjectStateManager.GetObjectStateEntry(entity);

        GP_PROJ_DBAudit audit = CreateAuditRecord(entry, AuditActions.I);

        Ents.SaveChanges();

        AddKeyFields(entry, audit);

        Ents.GP_PROJ_DBAudit.AddObject(audit);

        Ents.SaveChanges();

        return true;

    }

    else

    {

        return false;

    }

}

 

If we look closer to this function,  the first thing we do is add the object to the context. Then we get the entry in the objectstatemanager. We save the changes in the context, so our key field is filled in. then I create the audit record.

 

Below I used the AddRecord function to insert an entity.

 

public void InsertProject(GP_PROJ_Projects project)

{

    try

    {

        project.Created = DateTime.Now;

        AddRecord<GP_PROJ_Projects>(project);

    }

    catch (Exception er)

    {

        Ents.GP_PROJ_Projects.DeleteObject(project);

        throw new ArgumentException("Insert failed, please check your data!", er.InnerException);

    }

}

 

The AddKeyfields function and the creation of the audit record you can find in PART (1)