Welcome to the CodeSmith Community!

How to automatically populate audit columns

Support Forums

Feel free to ask any questions about CodeSmith here.

How to automatically populate audit columns

  • rated by 0 users
  • This post has 16 Replies |
  • 4 Followers
  • I am using Nettiers with Codesmith 5.2

    I have a bunch of classes/tables with the following columns:

    CreatedOn - Datetime - system date. Represents the date time of when the record was created

    CreatedBy - NVarchar(30) - userid of the logged in user that created the record

    ModifiedOn - Datetime - system date. Represents the date time of when the record was updated last.

    ModifiedBy - NVarchar(30) - userid of the logged in user that updated the record the last time

    How do I set these column values before saving objects? The four columns may or may not be there on the table. But they are, all four columns with the same names as documented above will be there.

    Thanks in advance.

     

  • I'd like to accomplish this generically by updating Codesmith Templates and/or writing some generic code.

  • You can see a discussion on this here:

    http://community.codesmithtools.com/forums/p/9266/34328.aspx

    Nettiers doesn't support this out of the box.  You would have to modify the templates in order for this to work.

    jeff

    ----------------------------------------------------------------------
     Member of the .NetTiers team | Visit http://www.nettiers.com
    ----------------------------------------------------------------------

  • I am thinking about modifying the EntityProviderBaseCore.generated.cst file and using reflection. The changed code is in bold

     

     

     

     

     

     

     

     

     

     

     

     

    public virtual Entity Save(TransactionManager mgr, Entity entity)
      {
       switch ( entity.EntityState )
       {
        case EntityState.Deleted:
         Delete(mgr, entity);
         break;
        case EntityState.Changed:
                        //get a list of all table columns
                        String[] Cols = entity.TableColumns;
                        //check if the column createdby exists

                        //using reflection set the column value
         Update(mgr, entity);
         break;
        case EntityState.Added:

                      //get a list of all table columns
                        String[] Cols = entity.TableColumns;
                        //check if the column createdby exists

                        //using reflection set the column value

         Insert(mgr, entity);
         break;
       }
       
       return entity;
      }

     

    Am I on the right track?

  • This is what I have for the code changes. I added a private method called SetAuditColmns.

      public virtual Entity Save(TransactionManager mgr, Entity entity)
      {
       switch ( entity.EntityState )
       {
        case EntityState.Deleted:
         Delete(mgr, entity);
         break;
        case EntityState.Changed:
                        SetAuditColumns(entity);
         Update(mgr, entity);
         break;
        case EntityState.Added:
                        SetAuditColumns(entity);
         Insert(mgr, entity);
         break;
       }
       
       return entity;
      }

    This is my new method.

     /// <summary>
            /// Sets audit column values
            /// </summary>
            /// <param name="entity"></param>
            /// <param name="entState"></param>
            private void SetAuditColumns(Entity entity)
            {

                Type MyEntityType = typeof(Entity);
                System.Reflection.FieldInfo CreatedOnFieldInfo = MyEntityType.GetField("createdOn");
                EntityState entState = entity.EntityState;

                DateTime ServerDateTime = NextGenDateUtil.ServerDateTime();

               if (CreatedOnFieldInfo != null)
                    {
                       CreatedOnFieldInfo.SetValue(entity, ServerDateTime);
                    }


                if (entState == EntityState.Changed)
                {
                    System.Reflection.FieldInfo ModifiedOnFieldInfo = MyEntityType.GetField("modifiedOn");

                    if (ModifiedOnFieldInfo != null)
                    {
                        ModifiedOnFieldInfo.SetValue(entity, ServerDateTime);
                    }

                }
            }

    When I debug my code, CreatedOnFieldInfo  and ModifiedByFieldInfo always are null in both on the entity as well as its base class. Using the ILDASM tool, I found that the columns (createdOn and modifiedOn) exist on the base class but those are not accesible via reflective methods.

    What am I missing?

     

     

     

     

  • Hello,

    Since you have then entity itself why don't you just set the values that it should be without reflection?

    entity.CreatedOn = DateTime.Now?

    Thanks

    -Blake Niemyjski

    Blake Niemyjski
    CodeSmith Tools, LLC. Software Development Engineer
    Blog: http://windowscoding.com/blogs/blake/
    .NetTiers team | Visit http://www.nettiers.net

  • Because not all entities will have createdon field. Thats why I have to do this reflectively.

  • I think I found a solution. And I have tested that it works. Can you confirm that this method will always be called whenever an entity is being saved?

     /// <summary>
            /// Sets audit column values
            /// This method is called from the save method only if
            /// the entity state is added or updated
            /// </summary>
            /// <param name="entity">The entity that is being created or updated</param>
            private void SetAuditColumns(Entity entity)
            {
                //pull the server date
                DateTime ServerDateTime = NextGenDateUtil.ServerDateTime();

                //build  a local array that will be used to invoke methods reflectively
                Object[] InputParms = { ServerDateTime };

                EntityState entState = entity.EntityState;

                //get the type of the entity
                Type MyEntityType = typeof(Entity);

                //set the created on only if this is a new entity
                if (entState == EntityState.Added)
                {
                    //get the created on setter property reflectively
                    MethodInfo MethodSetCreatedOn = MyEntityType.GetMethod("set_CreatedOn");

                    if (MethodSetCreatedOn != null)
                    {
                        MethodSetCreatedOn.Invoke(entity, InputParms);
                    }
                }

                    //get the modified setter property reflectively
                    MethodInfo MethodSetModifiedOn = MyEntityType.GetMethod("set_ModifiedOn");

                    //modified on will be set in both added and updated entity state scenarios
                    if (MethodSetModifiedOn != null)
                    {
                        MethodSetModifiedOn.Invoke(entity, InputParms);
                    }

            }

     

  • Hello,

    It should as long as you are calling Save for that specific entity.

    Thanks

    -Blake Niemyjski

    Blake Niemyjski
    CodeSmith Tools, LLC. Software Development Engineer
    Blog: http://windowscoding.com/blogs/blake/
    .NetTiers team | Visit http://www.nettiers.net

  • Thanks. In other words, as long as the we use Nettiers Save or DeepSave, we'll be fine. Is that correct?

  • Hello,

    It should work as long as you call Save, I would create a unit test for this to make sure that after each regeneration this is still working.

     

    Thanks

    -Blake Niemyjski

    Blake Niemyjski
    CodeSmith Tools, LLC. Software Development Engineer
    Blog: http://windowscoding.com/blogs/blake/
    .NetTiers team | Visit http://www.nettiers.net

  • Thanks. Two additional questions.

    1. I am trying modify the templates to incorporate these changes. The source file I changed for my testing is 'EntityProviderBaseCore.generated.cs'. Looks like the closest template to change is 'EntityProviderBaseCoreClass.generated.cst' and not EntityProviderBaseCore.generated.cst. Is this because this is a partial class? Is this the right template to change?

    2. Based on all the testing I have done, looks like my code is being called whether the developer called DeepSave or Save on any object. So as long as the developers use Nettiers to save objects (save or deepsave), we should be fine. Am I correct?

     

     

  • Hello,

     These templates are not documented, we have fixed this in .netTiers 3.0. The only way to check this is to look up the interfaces and regions defined in the code and find the matching template.

    Yes you are correct.

    Thanks

    -Blake Niemyjski

    Blake Niemyjski
    CodeSmith Tools, LLC. Software Development Engineer
    Blog: http://windowscoding.com/blogs/blake/
    .NetTiers team | Visit http://www.nettiers.net

  • Thanks. I got all my answers. I have changed the SetAuditColumns method  slightly.

    Let me summarize the solution.

    1.  Edit the 'EntityProviderBaseCoreClass.generated.cst' in "../netTiers Templates/DataAccessLayer/Bases" to include these two changes:

    a. Add a new method

    private void SetAuditColumns(Entity entity)
            {
                //pull the server date
                DateTime ServerDateTime = NextGenDateUtil.ServerDateTime();

                //pull the logged in user name from the config settings
                String UserName = System.Configuration.ConfigurationManager.AppSettings["LoggedinUser"].ToString();

                EntityState entState = entity.EntityState;

                //get the type of the entity
                Type MyEntityType = entity.GetType();

                //set the created on only if this is a new entity
                if (entState == EntityState.Added)
                {
                    //set the created on property
                    PropertyInfo CreatedOn = MyEntityType.GetProperty("CreatedOn");

                    if (CreatedOn != null)
                    {
                        CreatedOn.SetValue(entity, ServerDateTime, null);
                    }

                    //set the userid
                    PropertyInfo CreatedBy = MyEntityType.GetProperty("CreatedBy");
                    if (CreatedBy != null)
                    {
                        CreatedBy.SetValue(entity, UserName, null);
                    }

                }

                    //get the modified setter property reflectively
                    PropertyInfo ModifiedOn = MyEntityType.GetProperty("ModifiedOn");

                    //modified on will be set in both added and updated entity state scenarios
                    if (ModifiedOn != null)
                    {
                        ModifiedOn.SetValue(entity, ServerDateTime, null);
                    }


                    //set the userid
                    PropertyInfo ModifiedBy = MyEntityType.GetProperty("ModifiedBy");

                    if (ModifiedBy != null)
                    {
                        ModifiedBy.SetValue(entity, UserName, null);
                    }

            }

    b. Called this method from Save method.

    public virtual Entity <%= MethodNames.Save %>(TransactionManager mgr, Entity entity)
      {
       switch ( entity.EntityState )
       {
        case EntityState.Deleted:
         <%= MethodNames.Delete %>(mgr, entity);
         break;
        case EntityState.Changed:
            SetAuditColumns(entity);
         <%= MethodNames.Update %>(mgr, entity);
         break;
        case EntityState.Added:
         SetAuditColumns(entity);
         <%= MethodNames.Insert %>(mgr, entity);
         break;
       }
       
       return entity;
      }

     

  • I'd like to accomplish this generically by updating Codesmith Templates and/or writing some generic code.

    thanks

    robert

    web:Blackberry 9900 battery

Page 1 of 2 (17 items) 12