【问题标题】:Support SQL Server change tracking with Entity Framework 6使用 Entity Framework 6 支持 SQL Server 更改跟踪
【发布时间】:2017-04-15 03:13:11
【问题描述】:

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

完成

我唯一能想到的就是在EF生成的SQL前面加上上面的sql子句。虽然看起来,想要修改由 ORM 生成的 SQL 本身是有问题的。尽管如此,即使我想,我也不知道在哪里可以做到。 EF 命令拦截可以达到目的吗?

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

【问题讨论】:

  • 不确定我的问题是否正确 - 根据我的推断,只要通过 EF 完成更新,您就想执行 chnage_tracking。可能您可以为更新/添加/删除存储过程(具有您需要的跟踪),然后将其映射到 EF 实体。更多细节在这里-entityframeworktutorial.net/entityframework6/…。如果我读错了你的问题,请告诉我..

标签: c# sql-server entity-framework change-tracking


【解决方案1】:

很遗憾,Entity Framework 6 没有对 SQL Server 更改跟踪的内置支持。但是,它确实公开了拦截功能,使您能够在执行之前修改它生成的 SQL。虽然更改 ORM 生成的 SQL 需要谨慎且有充分的理由,但在某些情况下它绝对是合适的解决方案。

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

值得注意的是,该框架将在每个INSERTUPDATEDELETE 之前调用NonQueryExecuting,使其成为您挂钩更改跟踪的好地方。

作为一个简单的例子,考虑这个拦截器:

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)
    {
    }
}

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

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

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

IDbCommandInterceptor 界面上没有大量出色的文档,但 this MSDN article 是一个不错的起点。

【讨论】:

  • 这是我试图让它工作的方式,但无法做到。 Infact 根本无法让拦截器运行,然后由于健康状况不佳不得不离开工作。我一回来工作就会再试一次并在这里留下反馈,所以到目前为止,我还没有对此进行测试
【解决方案2】:

[在 OP 澄清后编辑]

我认为set up Change Tracking on the server side 更容易,而且不会与 EF 查询混淆。更改跟踪很快:

优点:

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

缺点:

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

[原答案]

一种方法是在您的“正常”更改中添加更改/跟踪信息,并将所有这些信息限定在单个事务中。

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

似乎有一个 Nuget 包可以帮助您进行审计 - TrackerEnabledDbContext

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

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

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

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

另一种方法是使用服务器端 (SQL) Change Data Capture,它会捕获对启用此功能的表所做的所有更改。 CDC 的一个重要方面是审计表结构发生变化时的行为。更多信息请阅读this article

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

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

【讨论】:

  • 这个问题是专门针对 SQL Server 变更跟踪的,为了清楚起见,让我更新一下问题
猜你喜欢
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 2011-07-01
  • 2017-09-05
  • 1970-01-01
相关资源
最近更新 更多