Tracker-enabled-dbcontext

Tracker-enabled DbContext offers you to implement full auditing in your database

What is tracker-enabled-dbcontext?

tracker-enabled-dbcontext is a .net library based on entity framework. It is created for tracking the changes in database. This library will records new record additions, record changes & record deletions. When recording record modifications, it will audit previous and new values of the fields. It will also audit the time of change and user who changed/added/deleted the record.

What are the types of tracker-enabled-dbcontext?

At present there are two kinds of tracker-enabled-dbcontext.

1. TrackerEnabledDbcontext

This is the main library. It can be used with any type of .net project. For example,

2. TrackerEnabledDbcontext.Identity

This library was later added to support Asp.Net Identity specifically.

You should install any one of them in one project depending upon the type of project type. You will generally not need both of them installed in project, but you can do it if you want.

How to install ?

Install the desired nuget package using nuget package manager or package manager console.

You can install using console, like this-

Install-Package TrackerEnabledDbContext

OR

Install-Package TrackerEnabledDbContext.Identity

If you want to try upcoming beta features, use the following command

Install-Package TrackerEnabledDbContext -Pre

How do I use it in my project ?

Important Note: This library only works with code first

For TrackerEnabledDbcontext,

public class Context : TrackerContext
{
    public DbSet<Car> Cars { get; set; }
}

For TrackerEnabledDbcontext.Identity,

public class ApplicationDbContext : TrackerIdentityContext<ApplicationUser>
{
    public ApplicationDbContext(string connectionString)
        : base(connectionString)
    {
    }

    public DbSet<Blog> Blogs { get; set; }
}

PS: Apart from these, many other of constructors are available for both kinds.

How to configure tracker to track entities?

In order to track entities, you will have to specify which entities you want to track. You can further specify which properties to track but it is optional. If you don't specify which properties to track, all properties of that entity will be tracked.

You can specify your tracking requirements in 3 ways.

  1. Annotations
  2. Fluent API
  3. Combination of both

With the recent introduction of Fluent API, it gives you more power to change/enable/disable tracking even on runtime.

Annotation based configuration

The following is an example of Comment class

[TrackChanges]
public class Comment
{
    public int Id { get; set; }

    [SkipTracking]
    public string Text { get; set; }

    public virtual int ParentBlogId { get; set; }

    public virtual Blog ParentBlog { get; set; }
}

By putting the annotation [TrackChanges], you specify that this entity should be tracked. But if you don't want to track the Text property, just add the annotation [SkipTracking].

This entity will have 3 columns in table. Id, Text and ParentBlogId. Although entity framework works even if you don't have the property 'ParentBlogId', this library will require you to have it if you wish to track foreign keys.

Fluent API configuration

If you don't like to put attributes on your entities, you can use the fluent api to configure tracking as following example.

        EntityTracker
            .TrackAllProperties<NormalModel>()
            .Except(x => x.Description)
            .And(x => x.Id);

Note that if you use both, annotations and fluent api, and they are conflicting for an entity or property, fluent api configuration will be considered high priority

Let's consider the following example where you have already configured tracking of a model with either annotations or fluent api. However now on runtime you want to override it for a specific property.

        EntityTracker
            .OverrideTracking<TrackedModelWithMultipleProperties>()
            .Disable(x => x.StartDate);

This way, all configuration is maintained and only StartDate tracking is disabled. You can enable it again using Enable() method.

At what levels can tracking be configured ?

You can configure tracking at 3 levels.

  1. Global
  2. Entity
  3. Property

Global Level example

Global level tracking is on by default but you can disable it at runtime as follows:

GlobalTrackingConfig.Enabled = false;

Entity Level example

Its very similar to Property level configuration -

for overriding entity level configuration,

        EntityTracker
            .TrackAllProperties<TrackedModelWithMultipleProperties>()
            .Except(x => x.Name)
            .And(x => x.Description);

        EntityTracker
            .OverrideTracking<TrackedModelWithMultipleProperties>()
            .Disable();

        EntityTracker
            .OverrideTracking<TrackedModelWithMultipleProperties>()
            .Enable();

In the above example, you specified tracking configuration for an entity and its properties. Then override the entity level tracking to be disabled while maintaining the tracking configuration and then enabled it again.

Note: While working with overrides, if you don't specify property, they work on entity on entity level

How is it tracked ?

context.SaveChanges(loggedInUser.Name);

OR

context.SaveChangesAsync(loggedInUser.Id);

All you have to do is, specify the username or userId while saving changes. However, maintain consistency in this pattern. Don't provide username at some places and userId at other places.

context.SaveChanges();

Even if you don't provide a username or userId, the entity will still be tracked. - Anonymously. This means that tracking data will be stored without a username / userid.

Where is my tracked data and how do I see/get it ?

Tracked data is stored in 2 tables

1. AuditLog

Stores entity level tracking information, like when an entity was changed, who changed it, what was the change ( insert /update/ delete ), etc.

2. AuditLogDetails

Stores property level tracking information like, if an entity property was modified what was its old value and what is its new value.

You have the freedom to query this tracking data manually as follows

You can also query the tracking data using built-in API as follows-

        using (Context ctx = new Context())
        {
            IQueryable<AuditLog> allCarLogs = ctx.GetLogs<Car>();

            Car myCar = ctx.Cars.Single(x => x.Number == "JH-876G");

            IQueryable<AuditLog> myCarLogs = ctx.GetLogs<Car>(myCar.Id);
        }

Be creative and figure out how you want to present this data on UI depending upon your project requirement. I have done my own DEMO implementation of UI which you can see in this sample project. it looks like this. It's pretty much useless implementation - just for demonstration.

With the data in 2 audit tables, much more can be done. For example,

  1. imagine creating a timeline UI that shows events in the life of an entity.
  2. or an API that takes an old datetime and reconstructs entity of that time using this tracking data and returns that entity.