"TimeStamp" Entities

41 views
Skip to first unread message

Krokonoster

unread,
Oct 13, 2012, 4:12:22 PM10/13/12
to sharp...@googlegroups.com
Title is a bit misleading.  Planning the following:

An "EntityInfo" class that inherit from "Enitity" with members "CreatedOn", "CreatedBy", "UpdatedOn", "UpdatedBy".
All enitiy classes inherit from "EntityInfo".

Where would be the best place to set these (if possible, avoiding duplication).
I'm thinking overriding the "SaveOrUpdate" method in my CUD classes?

Just been wondering how you'd deal with this.

Billy McCafferty

unread,
Oct 19, 2012, 3:16:37 PM10/19/12
to sharp...@googlegroups.com
Ah, you're wanting basic auditing capabilities...I do this frequently on my projects.  Here's how I do it:

#) Add IContextService to your project's .Core:

public interface IContextService
{
    User GetLoggedInUser();
    ...anything else...
}

----------------
#) Add ContextService to your .Web:

public class ContextService : IContextService
{
    public ContextService(IUserRepository userRepository) {
        Check.Require(userRepository != null, "userRepository may not be null");

        _userRepository = userRepository;
    }

    public User GetLoggedInUser() {
        // below may be adjusted to suit your authentication implementation
        string username = HttpContext.Current.User.Identity.Name;
        return _userRepository.GetByUsername(username);
    }
}

-----------
#) Add IAuditable to your .Core:

public interface IAuditable
{
    User CreatedBy { get; set; }
    DateTime? CreatedOn { get; set; }
    User LastUpdatedBy { get; set; }
    DateTime? LastUpdatedOn { get; set; }
}

-----------
#) Add Auditable to .Core:

public class AuditableEntity : Entity, IAuditable
{
    public virtual User CreatedBy { get; set; }
    public virtual DateTime? CreatedOn { get; set; }
    public virtual User LastUpdatedBy { get; set; }
    public virtual DateTime? LastUpdatedOn { get; set; }
}

-----------
#) Have your auditable entities inherit from AuditableEntity

-----------
#) Add AuditLogger to .Data (may need to be tweaked for latest S#arp Lite):

public class AuditLogger : DefaultSaveEventListener
{
    public AuditLogger() {
        _contextService = SafeServiceLocator<IContextService>.GetService();
    }

    protected override object PerformSaveOrUpdate(SaveOrUpdateEvent @event) {
        IAuditable auditable = @event.Entity as IAuditable;

        if (auditable != null) {
            UpdateWithAuditingInformation(auditable);
        }

        return base.PerformSaveOrUpdate(@event);
    }

    private void UpdateWithAuditingInformation(IAuditable auditable) {
        Check.Require(auditable as EntityWithTypedId<int> != null, 
            "The object being saved was expected to be an EntityWithTypedId<int> for auditing purposes");

        if ((auditable as EntityWithTypedId<int>).IsTransient()) {
            auditable.CreatedBy = _contextService.GetLoggedInUser();
            auditable.CreatedOn = DateTime.UtcNow;
        }

        auditable.LastUpdatedBy = _contextService.GetLoggedInUser();
        auditable.LastUpdatedOn = DateTime.UtcNow;
    }

    private readonly IContextService _contextService;
}

-----------
#) Add the listener to your NHibernate confg; below is an example if using XML for your NHibernate config:

<listener class="Flowan.Data.AuditLogger, Flowan.Data" type="save-update" />

Hope this helps!
Billy McCafferty

Billy McCafferty

unread,
Oct 19, 2012, 3:18:14 PM10/19/12
to sharp...@googlegroups.com
Ah yes, and don't forget to register ContextService with your IoC for instances of IContextService.  And you could also use IRepository<User> directly instead of wrapping GetByUsername with a custom IUserRepository.
Reply all
Reply to author
Forward
0 new messages