Регистрация каждого изменения данных с Entity Framework

entity-framework logging

Вопрос

Клиенту необходимо регистрировать каждое изменение данных в таблице журналов с фактическим пользователем, который внес изменение. Приложение использует одного пользователя SQL для доступа к базе данных, но нам нужно зарегистрировать «реальный» идентификатор пользователя.

Мы можем сделать это в t-sql, написав триггеры для каждой вставки и обновления таблицы и используя context_info для хранения идентификатора пользователя. Мы передали идентификатор пользователя в хранимую процедуру, сохранили идентификатор пользователя в contextinfo, и триггер мог использовать эту информацию для записи строк журнала в таблицу журнала.

Я не могу найти место или способ, где и как я могу сделать что-то подобное, используя EF. Итак, основная цель: если я внесу изменение в данные с помощью EF, я бы хотел записать точное изменение данных в таблицу полуавтоматическим способом (поэтому я не хочу проверять каждое поле на предмет изменений до сохранение объекта). Мы используем EntitySQL.

К сожалению, мы должны придерживаться SQL 2000, так что ввод изменений данных, представленный в SQL2008, не является вариантом (но, возможно, это также не правильный путь для нас).

Есть идеи, ссылки или отправные точки?

[Редактировать] Некоторые примечания: с помощью обработчика событий ObjectContext.SavingChanges я могу получить точку, в которой я могу ввести оператор SQL для инициализации contextinfo. Однако я не могу смешать EF и стандартный SQL. Таким образом, я могу получить EntityConnection, но не могу выполнить оператор T-SQL, используя его. Или я могу получить строку подключения EntityConnection и создать SqlConnection на ее основе, но это будет другое подключение, поэтому contextinfo не повлияет на сохранение, выполненное EF.

Я попробовал следующее в обработчике SavingChanges:

testEntities te = (testEntities)sender;
DbConnection dc = te.Connection;
DbCommand dcc = dc.CreateCommand();
dcc.CommandType = CommandType.StoredProcedure;
DbParameter dp = new EntityParameter();
dp.ParameterName = "userid";
dp.Value = textBox1.Text;
dcc.CommandText = "userinit";
dcc.Parameters.Add(dp);
dcc.ExecuteNonQuery();

Ошибка: значение EntityCommand.CommandText недопустимо для команды StoredProcedure. То же самое с SqlParameter вместо EntityParameter: SqlParameter не может быть использован.

StringBuilder cStr = new StringBuilder("declare @tx char(50); set @tx='");
cStr.Append(textBox1.Text);
cStr.Append("'; declare @m binary(128); set @m = cast(@tx as binary(128)); set context_info @m;");

testEntities te = (testEntities)sender;
DbConnection dc = te.Connection;
DbCommand dcc = dc.CreateCommand();
dcc.CommandType = CommandType.Text;
dcc.CommandText = cStr.ToString();
dcc.ExecuteNonQuery();

Ошибка: синтаксис запроса недействителен.

Итак, я застрял, чтобы создать мост между Entity Framework и ADO.NET. Если я смогу заставить это работать, я опубликую доказательство концепции.

Принятый ответ

Как насчет обработки контекста. SavingChanges ?


Популярные ответы

Спасибо, что указал мне правильное направление. Однако в моем случае мне также нужно установить контекстную информацию при выполнении операторов выбора, потому что я запрашиваю представления, которые используют контекстную информацию для управления безопасностью на уровне строк пользователем.

Я обнаружил, что проще всего присоединиться к событию StateChanged соединения и просто наблюдать за изменением с не открытого на открытое. Затем я вызываю proc, который устанавливает контекст, и он работает каждый раз, даже если EF решает сбросить соединение.

private int _contextUserId;

public void SomeMethod()
{
    var db = new MyEntities();
    db.Connection.StateChange += this.Connection_StateChange;
    this._contextUserId = theCurrentUserId;

    // whatever else you want to do
}

private void Connection_StateChange(object sender, StateChangeEventArgs e)
{
    // only do this when we first open the connection
    if (e.OriginalState == ConnectionState.Open ||
        e.CurrentState != ConnectionState.Open)
        return;

    // use the existing open connection to set the context info
    var connection = ((EntityConnection) sender).StoreConnection;
    var command = connection.CreateCommand();
    command.CommandText = "proc_ContextInfoSet";
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add(new SqlParameter("ContextUserID", this._contextUserId));
    command.ExecuteNonQuery();
}


Related

Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему