In last post showed how easy it was to use POCO classes in EntityFramework v4, a feature called “All Code”. This new feature, facilitates creating domain models but how about other features you may already have using other ORMs like NHibernate? Let’s see how we can achieve automatic audit logging in EF v4.

I’m using the bits included in Visual Studio 2010 Beta 2 with additional features included in Entity Framework 4 CTP 2 (not included in VS 2010) which you can download here.

For running Linq queries, we probably already have an existing ObjectContext implementation (see previous post). The ObjectContext class provides. To create audit log, we need to tap into SavingChanges event and populate audit properties. Having an “EntityBase* class as layer super-type that contains Inserted, InsertedBy, Updated and UpdatedBy audit fields we can add this cross cutting concern, across all of our entities which is what I’ve done here, but obviously instead of using a base class, you can do this with an Interface. Let’s create an AuditableObjectContext which will do the auditing part when the entity is saved by either an Update or an Insert:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class AuditableObjectContext : ObjectContext
{
public AuditableObjectContext(EntityConnection connection) : base(connection)
{
SubscribeEvents();
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
UnSubscribeEvents();
}

base.Dispose(disposing);
}

private void SubscribeEvents()
{
SavingChanges += OnSavingChangesCore;
}

private void UnSubscribeEvents()
{
SavingChanges -= OnSavingChangesCore;
}

private void OnSavingChangesCore(object sender, EventArgs e)
{
OnSavingChanges();
}

public virtual void OnSavingChanges()
{
FillAuditFields();
}

private void FillAuditFields()
{
var stateManager = ObjectStateManager;
var inserted = stateManager.GetObjectStateEntries(EntityState.Added);
var modified = stateManager.GetObjectStateEntries(EntityState.Modified);

inserted.Where(x => x.Entity is EntityBase).Select(x => x.Entity).Cast<EntityBase>().ForEach(x =>
{
x.Inserted = DateTime.Now;
x.InsertedBy = Thread.CurrentPrincipal.Identity.Name;
});

modified.Where(x => x.Entity is EntityBase).Select(x => x.Entity).Cast<EntityBase>().ForEach(x =>
{
x.Updated = DateTime.Now;
x.UpdatedBy = Thread.CurrentPrincipal.Identity.Name;
});
}
}

Our existing SalesObjectContext class from previous post, now looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SalesObjectContext : AuditableObjectContext
{
public SalesObjectContext(EntityConnection connection)
: base(connection)
{
}

public IObjectSet<Category> Categories
{
get { return CreateObjectSet<Category>(); }
}

public IObjectSet<Product> Products
{
get { return CreateObjectSet<Product>(); }
}
}

It was pretty easy and painless to create auditing fields, don’t you think? In the next post, we’ll see how to implement repository pattern using POCO mode and EF v4.