locked
Get default audit field behavior with an external datasource RRS feed

  • General discussion

  • Others have posted and blogged extensively about creating a robust audit trail for LightSwitch. However, if you are looking to achieve the default behavior with an external datasource, you could simply add the fields to your database and write code in every entity's Inserting() and Updating() method.  However, if you have many tables in your app this can be a lot of work.  Here is a very easy way to DRY this up. 

    1. Add the audit fields to your tables

    - CreatedBy

    - DateCreated

    - UpdatedBy

    - DateUpdated

    2. Use this code in the DataService class for your datasource.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.LightSwitch;
    using Microsoft.LightSwitch.Security.Server;
    
    namespace LightSwitchApplication
    {
        public partial class ApplicationDataService
        {
            partial void SaveChanges_Executing()
            {
                EntityChangeSet changes = this.Details.GetChanges();
                IReadOnlyCollection<IEntityObject> addedEntities = changes.AddedEntities;
                IReadOnlyCollection<IEntityObject> modifiedEntities = changes.ModifiedEntities;
    
                if (addedEntities.Any())
                {
                    foreach (IEntityObject entity in addedEntities)
                    {
                        InsertAuditFields(entity);
                    }
                }
    
                if (modifiedEntities.Any())
                {
                    foreach (IEntityObject entity in modifiedEntities)
                    {
                        UpdateAuditFields(entity);
                    }
                }
            }
    
            private void InsertAuditFields(IEntityObject entity)
            {
                string userName = this.Application.User.FullName;
                DateTimeOffset currentDateTime = DateTimeOffset.Now;
    
                entity.Details.Properties["CreatedBy"].Value = userName;
                entity.Details.Properties["DateCreated"].Value = currentDateTime;
                entity.Details.Properties["UpdatedBy"].Value = userName;
                entity.Details.Properties["DateUpdated"].Value = currentDateTime;
            }
    
            private void UpdateAuditFields(IEntityObject entity)
            {
                string userName = this.Application.User.FullName;
                DateTimeOffset currentDateTime = DateTimeOffset.Now;
    
                entity.Details.Properties["UpdatedBy"].Value = userName;
                entity.Details.Properties["DateUpdated"].Value = currentDateTime;
            }
        }
    }

    Hopefully this helps someone.

    Wednesday, December 31, 2014 2:10 PM

All replies

  • HI Hessc,

    Thanks for sharing your project and experience with us, I will try it on my side. :)

    Happy new year!

    Best regards,

    Angie 


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Thursday, January 1, 2015 7:49 AM
  • I have done similar things here: http://blog.pragmaswitch.com/?p=384

    The main difference is that I follow an "opt-in" approach by using an interface IAuditable which you can apply on entities where you desire auditing:

    public partial class Customer: IAuditable
        {
        }

    This ensures, in compile time that the table has actually the audit fields and it allows to exclude tables from auditing.

    Happy new year !


    paul van bladel ==independent enterprise application architect== http://blog.pragmaswitch.com

    Thursday, January 1, 2015 8:21 AM
  • Awesome!  Thanks Paul.
    Thursday, January 1, 2015 6:40 PM
  • This version will check whether the table has the audit properties, thus allowing you to opt in.  Paul's solution is going to be better in the long run because it checks at compile time.  This was meant to be a quick way to get the default behavior.  This is not a substitute for a full audit capability (see Paul's blog) if that is your requirement.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.LightSwitch;
    using Microsoft.LightSwitch.Security.Server;
    using Microsoft.LightSwitch.Details;
    
    namespace LightSwitchApplication
    {
        public partial class ApplicationDataService
        {
            partial void SaveChanges_Executing()
            {
                EntityChangeSet changes = this.Details.GetChanges();
                IReadOnlyCollection<IEntityObject> addedEntities = changes.AddedEntities;
                IReadOnlyCollection<IEntityObject> modifiedEntities = changes.ModifiedEntities;
    
                if (addedEntities.Any())
                {
                    foreach (IEntityObject entity in addedEntities)
                    {
                        if (AuditProperties(entity))
                        {
                            InsertAuditFields(entity);
                        }
                    }
                }
    
                if (modifiedEntities.Any())
                {
                    foreach (IEntityObject entity in modifiedEntities)
                    {
                        if (AuditProperties(entity))
                        {
                            UpdateAuditFields(entity);
                        }
                    }
                }
            }
    
            private bool AuditProperties(IEntityObject entity)
            {
                bool hasAuditProperties = true;
                bool createdBy = entity.Details.Properties.Contains("CreatedBy");
                bool dateCreated = entity.Details.Properties.Contains("DateCreated");
                bool updatedBy = entity.Details.Properties.Contains("UpdatedBy");
                bool dateUpdated = entity.Details.Properties.Contains("DateUpdated");
    
                bool[] checkForAuditProperties = new bool[] 
                    {
                        createdBy,
                        dateCreated,
                        updatedBy,
                        dateUpdated
                    };
    
                if (checkForAuditProperties.Any(a => a == false))
                {
                    hasAuditProperties = false;
                }
                return hasAuditProperties;
            }
    
            private void InsertAuditFields(IEntityObject entity)
            {
                string userName = this.Application.User.FullName;
                DateTimeOffset currentDateTime = DateTimeOffset.Now;
    
                entity.Details.Properties["CreatedBy"].Value = userName;
                entity.Details.Properties["DateCreated"].Value = currentDateTime;
                entity.Details.Properties["UpdatedBy"].Value = userName;
                entity.Details.Properties["DateUpdated"].Value = currentDateTime;
            }
    
            private void UpdateAuditFields(IEntityObject entity)
            {
                string userName = this.Application.User.FullName;
                DateTimeOffset currentDateTime = DateTimeOffset.Now;
    
                entity.Details.Properties["UpdatedBy"].Value = userName;
                entity.Details.Properties["DateUpdated"].Value = currentDateTime;
            }
        }
    }

    Thursday, January 1, 2015 7:58 PM