This one I had lying around for quite a while now, but I still think it’s a nice trick when you are working with ADO.NET entity framework inside your application. In a lot of applications there’s some way to track who created and/or modified entities inside the application. In this article I’m going to show a trick on how you can make use of the capabilities offered by ADO.NET entity framework to move the logic required to keep track of this kind of data into the datacontext instead of writing your own custom logic each time you want to save a new or modified entity to the database.
Important notice: Looks like I didn’t specify which framework this post was meant for. It’s meant for .NET framework 4. The main reason for this is that I’m able to override SaveChanges in this version.
Preparing for creation and modification tracking
The first step in making your data model tracking aware is to modify the entities generated from the ADO.NET entity framework model, so that the DataContext is able to identify some key fields on the entity.
For this I’ve create a new interface called ITrackableEntity. This interface contains four properties, CreatedBy,DateCreated,ModifiedBy and DateModifed.
1: public interface ITrackableEntity
2: {
3: string CreatedBy { get; set; }
4: string ModifiedBy { get; set; }
5: DateTime DateCreated { get; set; }
6: DateTime? DateModified { get; set; }
7: }
As a convention I’ve modeled my database in such a way that all tables have columns that have the same name as the properties I have defined on the interface. So I should be able to apply this interface to all the entities in my application.
To apply the new interface to the entities that were generated from the model I need to create a new C# code file. In this file I’m going to declare partial classes in the same namespace as the entities that were generated by the ADO.NET entity framework model. The following snippet demonstrates this.
1: public partial class Project : EntityObject, ITrackableEntity { }
2:
3: public partial class Release : EntityObject, ITrackableEntity { }
As you can see I’ve made all classes partial and derived them from EntityObject. I’ve also added my interface. This is all that you need to do to prepare the entities for the next step.
Writing the tracking code
The second step in the process is to modify the ObjectContext class generated by the ADO.NET entity framework model, so that the creation and modification information is applied to the entities before they are saved to the database.
To do this I need to create a new C# code file and again make a partial class, but this time it has the same name as the generated ObjectContext class and also it is defined in the same namespace as the generated ObjectContext class.
1: public partial class BugtrackerEntities : ObjectContext
2: {
3: public override int SaveChanges(SaveOptions options)
4: {
5: //TODO: Extend save logic here
6: return base.SaveChanges(options);
7: }
8: }
Within this new partial class I’m overriding the SaveChanges method so that I can extend the save logic with my own tracking logic.
Within the SaveChanges method I can ask the ObjectStateManager to supply me with all the entities that were created by the application. This will give me a set of entries which contain state information and the entity that was created. When I cast the Entity to ITrackableEntity which I created earlier I can now apply the creation information.
1: // Set the creation information before the new entities are saved in the database
2: foreach (ObjectStateEntry stateEntry in ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added))
3: {
4: var trackable = stateEntry.Entity as ITrackableEntity;
5:
6: if (trackable != null)
7: {
8: trackable.CreatedBy = Thread.CurrentPrincipal.Identity.Name;
9: trackable.DateCreated = DateTime.Now;
10: }
11: }
After I’ve updated the CreatedBy and DateCreated property I’m delegating the actual save operation back to the framework.The same can be done for entities that have been modified. The finished ObjectContext implementation looks like this.
1: public partial class BugtrackerEntities : ObjectContext
2: {
3: public override int SaveChanges(SaveOptions options)
4: {
5: // Set the modification information before the modified entities are saved in the database
6: foreach (ObjectStateEntry stateEntry in ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified))
7: {
8: var trackable = stateEntry.Entity as ITrackableEntity;
9:
10: if (trackable != null)
11: {
12: trackable.ModifiedBy = Thread.CurrentPrincipal.Identity.Name;
13: trackable.DateModified = DateTime.Now;
14: }
15: }
16:
17: // Set the creation information before the new entities are saved in the database
18: foreach (ObjectStateEntry stateEntry in ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added))
19: {
20: var trackable = stateEntry.Entity as ITrackableEntity;
21:
22: if (trackable != null)
23: {
24: trackable.CreatedBy = Thread.CurrentPrincipal.Identity.Name;
25: trackable.DateCreated = DateTime.Now;
26: }
27: }
28:
29: return base.SaveChanges(options);
30: }
31: }
Conclusion
The modifications described in this article make working with creation and modification information somewhat easier and cleaner. I really like ADO.NET entity framework because of the fact that it makes use of a central class that manages the save and load operations on data in the application. Because of this central class I can move all kinds of boilerplate logic involving change tracking etc. to this central class and don’t need to worry that I forget to put this logic in my application.
2 comments
So useful, I was looking for this for some time already. Thank you.
Bryan Fok
Mate, your name even pops up in google.com.au 😉 Thanks for your post!
Olaf