使用Entity Framework 6支持SQL Server更改跟踪

bil*_*der 16 c# sql-server entity-framework change-tracking

我有一个从现有SQL Server数据库生成的Entity Framework 6 Code First模型.数据库正在使用SQL Server更改跟踪,因此对于从EF生成的所有数据操作操作,我想设置更改跟踪上下文,以区别于其他外部进程所做的更改.这通常在T-SQL中完成
WITH CHANGE_TRACKING_CONTEXT (@source_id) UPDATE <table>...

我唯一能想到的是将上面的sql子句添加到EF生成的SQL中.虽然看来,想要修改ORM生成的SQL本身就值得怀疑.尽管如此,即使我想,我也不知道它可以在哪里完成.EF Command Interception可以达到目的吗?

问题是关于SQL Server的更改跟踪功能与EF一起使用(不是EF的更改跟踪).就EF而言,问题只是以编程方式修改EF生成的SQL

Rya*_*ann 7

不幸的是,Entity Framework 6没有内置的SQL Server Change Tracking支持.但是,它确实暴露了拦截功能,使您能够在执行之前修改它生成的SQL.在更改SQL时,ORM生成的内容是应该仔细进行的,并且只有充分的理由,绝对是合适的解决方案.

EF6公开了IDbCommandInterceptor类型,它为您提供了整个查询管道的钩子.您只需要实现此接口并使用EF注册拦截器.

值得注意的是,该框架将调用NonQueryExecuting每前INSERT,UPDATEDELETE,使其成为一个伟大的地方让你在你的更改跟踪挂钩.

举个简单的例子,考虑这个拦截器:

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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

当EF生成任何更改DB状态的查询时,它将在执行查询之前调用此方法.这使您有机会使用标准SQL注入自定义更改跟踪上下文.

要使用EF注册拦截器,只需DbInterception.Add在启动代码中的某处调用:

var changeTrackingInterceptor = new ChangeTrackingInterceptor();
DbInterception.Add(changeTrackingInterceptor);
Run Code Online (Sandbox Code Playgroud)

这个IDbCommandInterceptor界面上没有很多很棒的文档,但这篇MSDN文章是一个很好的起点.


Ale*_*xei 2

[OP澄清后编辑]

我认为在服务器端设置更改跟踪更容易,并且不会与 EF 查询混淆。简而言之,更改跟踪:

优点:

  • 轻的
  • 设置非常简单
  • 支持同步
  • 仍然是事务的一部分(如果事务失败则回滚)
  • 没有 SQL 代理依赖性
  • 将捕获正常 ORM 更改操作之外所做的更改(由您的代码调用的存储过程,源自应用程序外部的所有更改)

缺点:

  • 不适合需要更多信息的审计
  • 有点慢,因为它是同步完成的(与 CDC 异步性质相反)

[原答案]

一种方法是在“正常”更改中添加更改/跟踪信息,并将所有更改范围限制在单个事务中。

您可以重写您的DbContext方法SaveChanges并添加跟踪信息的代码。ChangeTracker 参考允许您查找具有特定状态(添加、更新、删除、未修改)的所有实体,从而还能够保存所执行的更改类型。这里提供了一个完整的工作示例。

Nuget 包似乎可以帮助您进行监听 - TrackerEnabledDbContext

这种方法的优点是您可以轻松标记实体类型,以便某些信息不会被审核(实现接口或使用某些自定义属性)。

另一个优点是,您可以通过在属性上显式指定跟踪属性来进一步微调更改跟踪。

我看到的一个缺点是事务会更长并且锁定某些表会更长,可能会导致性能问题(这在很大程度上取决于每个时间段的事务数量)。

此外,此解决方案将仅捕获上下文代码 (EF) 中的更改,而不捕获直接针对数据库或通过存储过程执行的其他更改(无论它们是从外部进程或 EF 调用的)。

另一种方法是使用服务器端 (SQL)更改数据捕获,捕获启用此功能的表上所做的所有更改。CDC 的一个重要方面是审计表结构发生更改时的行为。欲了解更多信息,请阅读这篇文章

服务器端方法有两个主要优点:

  • 更快,因为它是异步完成的
  • 如果数据更改来自各种来源(手动、ETL、存储过程等),则更可靠。