Поддержка отслеживания изменений SQL Server с помощью Entity Framework 6
У меня есть модель Entity Framework 6 Code First, созданная из существующей базы данных SQL Server. В базе данных используется отслеживание изменений SQL Server, поэтому для всех операций обработки данных, генерируемых из EF, я хочу установить контекст отслеживания изменений, чтобы отличать их от изменений, сделанных другими внешними процессами. Обычно это делается в T-SQL как
WITH CHANGE_TRACKING_CONTEXT (@source_id) UPDATE <table>...
Единственное, о чем я могу думать, - добавить вышеприведенное предложение sql к SQL, сгенерированному EF. Хотя кажется, что желание изменить SQL, сгенерированный ORM, сомнительно. Тем не менее, даже если я хочу, я не знаю, где это можно сделать. Может ли EF Command Interception служить цели?
В частности, речь идет о функции отслеживания изменений SQL Server наряду с EF (не отслеживанием изменений EF). Что касается EF, то речь идет только о программном изменении SQL, сгенерированном EF
Ответы
Ответ 1
К сожалению, Entity Framework 6 не имеет встроенной поддержки отслеживания изменений SQL Server. Тем не менее, он предоставляет возможности перехвата, которые позволяют вам изменять SQL, который он создает до выполнения. При изменении SQL, создаваемого ORM, это то, что нужно делать осторожно и только по уважительной причине, есть абсолютно случаи, когда это подходящее решение.
EF6 предоставляет тип IDbCommandInterceptor
, который дает вам крючки во весь конвейер запросов. Вам просто необходимо реализовать этот интерфейс и зарегистрировать ваш перехватчик с помощью EF.
Примечательно, что фреймворк будет вызывать NonQueryExecuting
перед каждым INSERT
, UPDATE
и DELETE
, что делает его отличным местом для вашего отслеживания изменений.
В качестве простого примера рассмотрим этот перехватчик:
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 генерирует любой запрос, который изменяет состояние БД, он будет вызывать этот метод перед выполнением запроса. Это дает вам возможность ввести свой настраиваемый контекст отслеживания изменений с помощью стандартного SQL.
Чтобы зарегистрировать ваш перехватчик с помощью EF, просто вызовите DbInterception.Add
где-то в вашем стартовом коде:
var changeTrackingInterceptor = new ChangeTrackingInterceptor();
DbInterception.Add(changeTrackingInterceptor);
Там нет большой документации по интерфейсу IDbCommandInterceptor
, но эта статья MSDN - это хорошее место для начала.
Ответ 2
[edit после разъяснения ОП]
Я думаю, что настроить отслеживание изменений на стороне сервера и не путаться с запросами EF. В ближайшее время изменится отслеживание изменений:
Плюсы:
- легкий
- настройка довольно проста
- поддержка синхронизации
- все еще часть транзакции (откат в случае сбоя транзакции)
- отсутствие зависимости агента SQL.
- будет ловить изменения, выполненные вне обычной операции изменения ORM (хранимые процедуры, вызываемые вашим кодом, все изменения, происходящие вне вашего приложения)
Минусы:
- не подходит для аудита, требующего дополнительной информации
- немного медленнее, потому что он выполняется синхронно (в отличие от асинхронного характера CDC).
[оригинальный ответ]
Один из способов - добавить информацию об изменениях/отслеживании в соответствии с вашими "нормальными" изменениями и охватить все из них в рамках одной транзакции.
Вы можете переопределить свой метод DbContext
SaveChanges
и добавить код для отслеживания информации. Ссылка ChangeTracker позволяет найти все объекты, которые имеют определенное состояние (добавлено, обновлено, удалено, немодифицировано) и, таким образом, также может сохранить тип выполненных изменений. Полный рабочий пример представлен здесь.
Кажется, что пакет Nuget поможет вам в аудите - TrackerEnabledDbContext
Преимущество такого подхода заключается в том, что вы легко указываете свои типы сущностей, чтобы некоторая информация не проверялась (реализовать интерфейс или использовать какой-то пользовательский атрибут).
Еще одно преимущество заключается в том, что вы можете более точно настроить отслеживание изменений, явно указав атрибуты отслеживания в своих свойствах.
Недостаток, который я вижу, заключается в том, что транзакции будут длиннее, а блокировка некоторых таблиц будет более продолжительной, что может привести к проблемам с производительностью (это сильно зависит от количества транзакций за период времени).
Кроме того, это решение будет ловить только изменения из вашего контекстного кода (EF), а не другие изменения, которые выполняются непосредственно с базой данных или с помощью хранимых процедур (независимо от того, что они вызываются из внешнего процесса или EF).
Другой подход заключается в использовании серверной части (SQL) Change Data Capture, которая ловит все изменения, внесенные в таблицы, для которых включена эта функция. Одним из важных аспектов CDC является поведение, когда изменяется структура проверенной таблицы. Для получения дополнительной информации прочтите в этой статье.
Подход на стороне сервера имеет два основных преимущества:
- быстрее, потому что выполняется асинхронно
- более надежный, если изменения данных поступают из разных источников (вручную, ETL, хранимая процедура и т.д.).