简体   繁体   English

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

[英]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. 我有一个从现有SQL Server数据库生成的Entity Framework 6 Code First模型。 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. 数据库正在使用SQL Server更改跟踪,因此对于从EF生成的所有数据操作操作,我想设置更改跟踪上下文,以区别于其他外部进程所做的更改。 This is usually done in T-SQL as 这通常在T-SQL中完成
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. 我唯一能想到的是将上面的sql子句添加到EF生成的SQL中。 Though it appears, wanting to modify SQL generated by an ORM is questionable itself. 虽然看来,想要修改ORM生成的SQL本身就值得怀疑。 Still, even if I want to, I don't know where it can be done. 尽管如此,即使我想,我也不知道它可以在哪里完成。 Can EF Command Interception serve the purpose? EF Command Interception可以达到目的吗?

The question is specifically about SQL Server's Change Tracking feature's use alongside EF (not EF's change tracking). 问题是关于SQL Server的更改跟踪功能与EF一起使用(不是EF的更改跟踪)。 In terms of EF, the question is only about programmatically modifying SQL generated by EF 就EF而言,问题只是以编程方式修改EF生成的SQL

Unfortunately, Entity Framework 6 does not have built-in support for SQL Server Change Tracking. 不幸的是,Entity Framework 6没有内置的SQL Server Change Tracking支持。 However, it does expose interception capabilities that enable you to modify the SQL it generates before execution. 但是,它确实暴露了拦截功能,使您能够在执行之前修改它生成的SQL。 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. 在更改SQL时,ORM生成的内容是应该仔细进行的,并且只有充分的理由,绝对是合适的解决方案。

EF6 exposes the IDbCommandInterceptor type, which gives you hooks into the entire query pipeline. EF6公开了IDbCommandInterceptor类型,它为您提供了整个查询管道的钩子。 You simple need to implement this interface and register your interceptor with EF. 您只需要实现此接口并使用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. 值得注意的是,该框架将在每次INSERTUPDATEDELETE之前调用NonQueryExecuting ,使其成为您加入更改跟踪的好地方。

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. 当EF生成任何更改DB状态的查询时,它将在执行查询之前调用此方法。 This gives you an opportunity to inject your custom change tracking context using standard SQL. 这使您有机会使用标准SQL注入自定义更改跟踪上下文。

To register your interceptor with EF, simply call DbInterception.Add somewhere in your startup code: 要使用EF注册拦截器,只需在启动代码中的某处调用DbInterception.Add

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. 关于IDbCommandInterceptor接口的文档并不多,但这篇MSDN文章是一个很好的起点。

[edit after OP's clarification] [OP澄清后编辑]

I think it is easier to set up Change Tracking on the server side and not mess with EF queries. 我认为在服务器端设置更改跟踪更容易,而不是弄乱EF查询。 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 没有SQL代理依赖
  • will catch changes done outside normal ORM change operation (stored procedures called by your code, all changes originating outside your application) 将捕获在正常ORM更改操作之外完成的更改(代码调用的存储过程,所有更改源自应用程序之外)

Cons: 缺点:

  • not fit for auditing that requires more information 不适合需要更多信息的审核
  • a little slower, because it it done synchronously (as opposed to CDC async nature) 慢一点,因为它同步完成(与CDC异步性质相反)

[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. 您可以覆盖DbContextSaveChanges方法并添加跟踪信息的代码。 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. ChangeTracker引用允许您查找具有特定状态(添加,更新,删除,未修改)的所有实体,从而能够保存所执行的更改类型。 A full working example is provided here . 这里提供一个完整的工作示例。

A Nuget package seems to be there to help you with the auding - TrackerEnabledDbContext 一个Nuget包似乎可以帮助你进行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). 此外,此解决方案将仅捕获您的上下文代码(EF)中的更改,而不会捕获直接针对数据库或通过存储过程执行的其他更改(无论是从外部进程还是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. 另一种方法是使用服务器端(SQL) 更改数据捕获 ,捕获对启用了此功能的表所做的所有更改。 One important aspect of CDC is the behavior when the audited table structure is changes. CDC的一个重要方面是审计表结构发生变化时的行为。 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.). 更可靠,如果数据变化来自各种来源(手动,ETL,存储过程等)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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