简体   繁体   中英

Support SQL Server change tracking with Entity Framework 6

I have an Entity Framework 6 Code First model generated from an existing SQL Server database. The database is using SQL Server Change Tracking, so for all the data manipulation operations generating from EF, I want to set the Change Tracking context to distinguish these from changes made by other external processes. This is usually done in T-SQL as
WITH CHANGE_TRACKING_CONTEXT (@source_id) UPDATE <table>...

The only thing I can think of is to prepend the above sql clause to the SQL generated by EF. Though it appears, wanting to modify SQL generated by an ORM is questionable itself. Still, even if I want to, I don't know where it can be done. Can EF Command Interception serve the purpose?

The question is specifically about SQL Server's Change Tracking feature's use alongside EF (not EF's change tracking). In terms of EF, the question is only about programmatically modifying SQL generated by EF

Unfortunately, Entity Framework 6 does not have built-in support for SQL Server Change Tracking. However, it does expose interception capabilities that enable you to modify the SQL it generates before execution. While changing the SQL an ORM generates is something that should be done carefully and only with good reason, there are absolutely cases where it is the appropriate solution.

EF6 exposes the IDbCommandInterceptor type, which gives you hooks into the entire query pipeline. You simple need to implement this interface and register your interceptor with EF.

Notably, the framework will call NonQueryExecuting before every INSERT , UPDATE , and DELETE , making it a great place for you to hook in your change tracking.

As a simple example, consider this interceptor:

public class ChangeTrackingInterceptor : IDbCommandInterceptor
{
    private byte[] GetChangeTrackingContext()
    {
        // TODO: Return the appropriate change tracking context data
        return new byte[] { 0, 1, 2, 3 };
    }

    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        command.CommandText = "WITH CHANGE_TRACKING_CONTEXT (@change_tracking_context)\r\n" + command.CommandText;

        // Create the varbinary(128) parameter
        var parameter = command.CreateParameter();
        parameter.DbType = DbType.Binary;
        parameter.Size = 128;
        parameter.ParameterName = "@change_tracking_context";
        parameter.Value = GetChangeTrackingContext();
        command.Parameters.Add(parameter);
    }

    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
    }

    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
    }

    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
    }

    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
    }
}

When EF generates any query that changes the state of the DB, it will call this method before executing the query. This gives you an opportunity to inject your custom change tracking context using standard SQL.

To register your interceptor with EF, simply call DbInterception.Add somewhere in your startup code:

var changeTrackingInterceptor = new ChangeTrackingInterceptor();
DbInterception.Add(changeTrackingInterceptor);

There's not a ton of great documentation on the IDbCommandInterceptor interface, but this MSDN article is a good place to start.

[edit after OP's clarification]

I think it is easier to set up Change Tracking on the server side and not mess with EF queries. Change Tracking shortly put:

Pros:

  • lightweight
  • setup is quite easy
  • support for synchronization
  • still part of the transaction (rolled back if transaction fails)
  • no SQL Agent dependency
  • will catch changes done outside normal ORM change operation (stored procedures called by your code, all changes originating outside your application)

Cons:

  • not fit for auditing that requires more information
  • a little slower, because it it done synchronously (as opposed to CDC async nature)

[original answer]

One way is to add change/tracking information along your "normal" changes and to have all of them scoped within a single transaction.

You can override your DbContext 's SaveChanges method and add the code for tracking information. ChangeTracker reference allows you to find all entities that have a certain state (added, updated, deleted, unmodified) and thus being able to also save the type of change performed. A full working example is provided here .

A Nuget package seems to be there to help you with the auding - TrackerEnabledDbContext

An advantage of this approach is that you easily mark your entity types, so that some information is not audited (either implement an interface or use some custom attribute).

Another advantage is that you can fine tune change tracking even more by explicitly specify tracking attributes on your properties.

A disadvantage I see is that the transactions will be longer and locking some tables will be longer, possibly leading to performance problems (this heavily depends on the number of transactions per period of time).

Also, this solution will catch only changes from your context code (EF) and not other changes that are performed directly against the database or through stored procedures (regardless of the fact that they are called from an external process or EF).

Another approach is to use server side (SQL) Change Data Capture that catches all changes made on tables that have this feature enabled for. One important aspect of CDC is the behavior when the audited table structure is changes. For more information read this article .

Server side approach has two major advantages:

  • is faster because it is done asynchronously
  • more reliable, if data changes come from various sources (manually, ETL, stored procedure etc.).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM