Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing OnContextCreated for auditing by assigning SaveChanges an eventhandler using EF 4.1 DBContext

I have tried many different ways and looked at different posts, but still haven't come across a solution for this way of auditing. Below is my DBContext template file. I customised it by adding the OnContextCreated() partial method and assign the SavingChanges event to my OnSavingChanges event handler.

namespace ARSystem.Models
{
    public partial class ARSEntities : ObjectContext
    {
        public ARSEntities()
            : base("name=ARSEntities")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }

        public string UserName { get; set; }
        List<DBAudit> auditTrailList = new List<DBAudit>();

        public enum AuditActions
        {
            I,
            U,
            D
        }

        partial void OnContextCreated()
        {
            this.SavingChanges += new EventHandler(OnSavingChanges);
        }

        public void OnSavingChanges(object sender, EventArgs e)
        {
            IEnumerable<ObjectStateEntry> changes = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified);
            foreach (ObjectStateEntry stateEntryEntity in changes)
            {
                if (!stateEntryEntity.IsRelationship &&
                        stateEntryEntity.Entity != null &&
                            !(stateEntryEntity.Entity is DBAudit))
                {//is a normal entry, not a relationship
                    DBAudit audit = this.AuditTrailFactory(stateEntryEntity, UserName);
                    auditTrailList.Add(audit);
                }
            }

            if (auditTrailList.Count > 0)
            {
                foreach (var audit in auditTrailList)
                {//add all audits 
                    this.AddToDBAudit(audit);
                }
            }
        }

        private DBAudit AuditTrailFactory(ObjectStateEntry entry, string UserName)
        {
            DBAudit audit = new DBAudit();
            audit.AuditId = Guid.NewGuid().ToString();
            audit.RevisionStamp = DateTime.Now;
            audit.TableName = entry.EntitySet.Name;
            audit.UserName = UserName;

            if (entry.State == EntityState.Added)
            {//entry is Added 
                audit.NewData = GetEntryValueInString(entry, false);
                audit.Actions = AuditActions.I.ToString();
            }
            else if (entry.State == EntityState.Deleted)
            {//entry in deleted
                audit.OldData = GetEntryValueInString(entry, true);
                audit.Actions = AuditActions.D.ToString();
            }
            else
            {//entry is modified
                audit.OldData = GetEntryValueInString(entry, true);
                audit.NewData = GetEntryValueInString(entry, false);
                audit.Actions = AuditActions.U.ToString();

                IEnumerable<string> modifiedProperties = entry.GetModifiedProperties();
                //assing collection of mismatched Columns name as serialized string 
                audit.ChangedColumns = XMLSerializationHelper.XmlSerialize(modifiedProperties.ToArray());
            }

            return audit;
        }

        private string GetEntryValueInString(ObjectStateEntry entry, bool isOrginal)
        {
            if (entry.Entity is EntityObject)
            {
                object target = CloneEntity((EntityObject)entry.Entity);
                foreach (string propName in entry.GetModifiedProperties())
                {
                    object setterValue = null;
                    if (isOrginal)
                    {
                        //Get orginal value 
                        setterValue = entry.OriginalValues[propName];
                    }
                    else
                    {
                        //Get orginal value 
                        setterValue = entry.CurrentValues[propName];
                    }
                    //Find property to update 
                    PropertyInfo propInfo = target.GetType().GetProperty(propName);
                    //update property with orgibal value 
                    if (setterValue == DBNull.Value)
                    {//
                        setterValue = null;
                    }
                    propInfo.SetValue(target, setterValue, null);
                }//end foreach

                XmlSerializer formatter = new XmlSerializer(target.GetType());
                XDocument document = new XDocument();

                using (XmlWriter xmlWriter = document.CreateWriter())
                {
                    formatter.Serialize(xmlWriter, target);
                }
                return document.Root.ToString();
            }
            return null;
        }

        public EntityObject CloneEntity(EntityObject obj)
        {
            DataContractSerializer dcSer = new DataContractSerializer(obj.GetType());
            MemoryStream memoryStream = new MemoryStream();

            dcSer.WriteObject(memoryStream, obj);
            memoryStream.Position = 0;

            EntityObject newObject = (EntityObject)dcSer.ReadObject(memoryStream);
            return newObject;
        }

        public DbSet<Student> Students { get; set; }
        public DbSet<User> Users { get; set; }
        public DbSet<aspnet_Applications> aspnet_Applications { get; set; }
        public DbSet<aspnet_Membership> aspnet_Membership { get; set; }
        public DbSet<aspnet_Roles> aspnet_Roles { get; set; }
        public DbSet<aspnet_SchemaVersions> aspnet_SchemaVersions { get; set; }
        public DbSet<aspnet_Users> aspnet_Users { get; set; }
        public DbSet<vw_aspnet_Applications> vw_aspnet_Applications { get; set; }
        public DbSet<vw_aspnet_MembershipUsers> vw_aspnet_MembershipUsers { get; set; }
        public DbSet<vw_aspnet_Roles> vw_aspnet_Roles { get; set; }
        public DbSet<vw_aspnet_Users> vw_aspnet_Users { get; set; }
        public DbSet<vw_aspnet_UsersInRoles> vw_aspnet_UsersInRoles { get; set; }
        public DbSet<Cours> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Modules> Modules { get; set; }
        public DbSet<EnrollmentsByCourse> EnrollmentsByCourse { get; set; }
        public DbSet<EnrollmentsByCourseAudit> EnrollmentsByCourseAudit { get; set; }
        public DbSet<DBAudit> DBAudit { get; set; }
    }
}

However, when I compile, i get the error message that:

Error 1 'ARSystem.Models.ARSEntities.OnModelCreating(System.Data.Entity.DbModelBuilder)': no suitable method found to override C:\Users\mngum\Documents\Visual Studio 2010\Projects\ARSystem\ARSystem\Models\ARSystem.Context.cs 35 33 ARSystem

I cannot see the OnContextCreated method in the DBContext metadata class but i can find it in the edmx designer. Please let me know how i can implement the OnContextCreated() method such that I can override the SavingChanges event for auditing purposes.

like image 658
user1549269 Avatar asked Feb 20 '23 18:02

user1549269


1 Answers

DbContext does not have an OnContextCreated event but that's not a problem because you don't need it to achieve the same. Instead with DbContext the SaveChanges method is overridable. So instead of your OnSavingChanges event handler you use:

public override int SaveChanges()
{
    // custom code...

    return base.SaveChanges();
}

This method will be called whenever you call ARSEntities.SaveChanges() and you can perform custom actions before you call the base.SaveChanges() of the base DbContext (ARSEntities must be derived from DbContext of course.)

You also can access the underlying ObjectContext from the DbContext:

public override int SaveChanges()
{
    var objectContext = ((IObjectContextAdapter)this).ObjectContext;

    // use methods and properties of ObjectContext now like
    // objectContext.ObjectStateManager, etc.

    // custom code...

    return base.SaveChanges();
}

Here was a similar question and answer about change auditing with EF 4.1/DbContext:

Entity Framework 4.1 DbContext Override SaveChanges to Audit Property Change

like image 137
Slauma Avatar answered May 13 '23 20:05

Slauma