Мы используем DiagnosticListeners
для изменения текста команды SQL, создаваемого EF Core. Проблема заключается в том, что наши слушатели должны изменять команду SQL на основе некоторых пользовательских данных, которые поступают в наш API через HttpRequests. Наше текущее решение чрезвычайно хакерское и может вызвать проблемы в будущем. Мы регистрируем нового слушателя каждый раз, когда создается DbContext
:
public class MyContext : DbContext
{
private readonly HReCommandAdapter _adapter;
public MyContext(DbContextOptions options) : base(options)
{
_adapter = new DbCommandAdapter();
var listener = this.GetService<DiagnosticSource>();
(listener as DiagnosticListener).SubscribeWithAdapter(_adapter);
}
public override void Dispose()
{
_adapter.Dispose();
base.Dispose();
}
//DbSets and stuff
}
Упрощенный код слушателя выглядит следующим образом:
public class DbCommandAdapter : IDisposable
{
private bool _hasExecuted = false;
private Guid? _lastExecId = null;
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting")]
public void OnCommandExecuting(DbCommand command, DbCommandMethod executeMethod, Guid commandId, Guid connectionId, bool async, DateTimeOffset startTime)
{
if (!_lastExecId.HasValue)
_lastExecId = connectionId;
if (_lastExecId != connectionId)
return;
//We are modifying command text here
}
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted")]
public void OnCommandExecuted(object result, bool async)
{
}
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandError")]
public void OnCommandError(Exception exception, bool async)
{
}
public void Dispose() { //No code in here }
}
Как видите, наш текущий подход заключается в использовании connectionId
, который будет отличаться при каждом создании DbContext
. Причина этого хакерского подхода заключается в том, что экземпляры слушателя не располагаются, даже если DbContext.Dispose()
вызывается каждый раз, когда обрабатывается HttpRequest
. Таким образом, connectionId
позволяет создать иллюзию отображения 1: 1 между слушателем и данным экземпляром DbContext
.
Что происходит, однако, так это то, что количество экземпляров слушателя накапливается в течение всего времени жизни API, и единственный раз, когда экземпляры уходят, это когда пулы приложений останавливаются или перезагружаются.
Можно ли как-то избавиться от этих экземпляров слушателя и как? Я также открыт к другому подходу для изменения команд SQL (диагностические прослушиватели были единственными жизнеспособными, которые мы нашли для EF Core).
EDIT:
Я изменяю только команды SELECT
. Я опустил детали, но DbCommandAdapter
создается с префиксом, специфичным для пользователя, который отличается в зависимости от пользователя, пытающегося получить доступ к API.
Например, если запрос:
SELECT FIELD1, FIELD2 FROM EMPLOYEES
и пользовательский префикс USER_SOMENUMBER
, тогда измененный запрос заканчивается:
SELECT FIELD1, FIELD2 FROM USER_SOMENUMBER_EMPLOYEES
Я понимаю, что это хрупко, но мы гарантируем, что схема изменяемого нами имени таблицы идентична, и это не проблема.