Building an Audit Log with nHibernate Listeners in AbleCommerce

by Joe Payne 13. March 2015 15:01

I have a client who can't seem to figure out who's making edits to certain products. Somebody changed the price, somebody marked it hidden. That sort of thing. Too many people in the back-end and no audit trail for who changed what.

So I decided to dig into nHibernate listeners. After a brutal all-nighter reading StackOverflow posts and copying snippets from a few blog posts, I actually got it working.

I added some new handlers for the Post-Commit-Create, Post-Commit-Update and Post-Commit-Delete listener events and pointed them to my AuditEventListener class. These are easily wired up in the DatabaseConfiguration.cs file.

I then created a simple class with no methods called IEntityToAudit. This is used to mark which specific data classes I want to audit.

In my AuditEventListener, I can tell if the class that fired the event is marked for audit by simply trying to cast it to the IEntityToAudit interface class. If the cast comes back not-null, I know that class is flagged to be audited.

var entityToAudit = @event.Entity as IEntityToAudit;
 
 if (entityToAudit != null)
 {
   
Now it's a simple matter of identifying the class being audited so I can record the name. And nHibernate makes it easy to tell what properties of the class are dirty.
 
This way the logging accomplishes two goals:
1. Only logging what properties were changed
2. Logging both the old value and new value of each changed property
 
I built a new data class to store the log entries in a new table in SQL. That made it easy to create a custom report in admin to view the audit log. I'm also going to build out a new button on the edit-category and edit-product pages. This will permit the admin to quickly view the audit log for a given entity.
 
Entities that are deleted record both the entityId and the value of the Name property (if that property exists).
 
To add logging to any entity in Able, I simply modify that <entity>.cs file and change 1 line of code to make it implement the IEntityToAudit interface. Everything else is handled automatically and outside of Able code to minimum customization of existing files.
 
Overall, it was a fun learning experience into the depths of nHibernate and the AbleCommerce domain model implementation.

Tags: , , ,

AbleCommerce Gold | Personal

NHibernate sub query on Orders filtering by usergroup

by Joe Payne 17. March 2014 09:31

A client had a need to filter the Monthly Sales Summary report by group membership.  In other words, restrict the totals to orders placed by users only in a specific user group.

The initial NHibernate query is such:

ICriteria criteria = NHibernateHelper.CreateCriteria<CommerceBuilder.Orders.Order>("O")
    .CreateCriteria("Items", "OI", NHibernate.SqlCommand.JoinType.InnerJoin)
    .Add(Restrictions.Eq("O.Store", AbleContext.Current.Store));

 

So I added a dropdown that is populated by GroupDataSource.LoadAll().  Then it seemed easy enough to just add this code which I found in the UserSearchCriteria.cs file:

if (groupId >= 0)
{
    criteria.CreateCriteria("O.User.UserGroups", "UG", NHibernate.SqlCommand.JoinType.InnerJoin)
    .Add(Restrictions.Eq("UG.Id.Group.Id", this.GroupId));
}

The problem is, this code throws a big ol’ NHibernate error “nhibernate multi-part identifier <someobject> could not be bound”

For some reason, NHibernate couldn’t resolve the relationship of Order –> User –> UserGroups –> Group.   The code I swiped from UserSearchCriteria had no problem with it.  But here it just wouldn’t work.

I finally figured out the solution was to create a new reference to the User table and base the criteria from there.   So instead of starting at the Order object level, the join and restriction starts as the User object level which sort of makes sense now that I’m typing this…

if (groupId >= 0)
{
    criteria.CreateCriteria("O.User", "U", NHibernate.SqlCommand.JoinType.InnerJoin)
        .CreateCriteria("U.UserGroups", "UG")
        .Add(Restrictions.Eq("UG.Id.Group.Id", groupId));
}

Finally, the end result is the NHibernate query now properly searches the orders for only those records where the user that placed the order is a member of a specific GroupId.

Tags: , , , ,

AbleCommerce Gold | Personal

Month List