Какова наилучшая практика для получения текущего имени пользователя на уровне доступа к данным?
-
22-09-2019 - |
Вопрос
Недавно мы добавили аудит в нашу базу данных.Коллега реализовал это с помощью триггеров и попросил меня вызвать хранимую процедуру при входе на веб-сайт.Хранимая процедура вставляет текущее имя пользователя и текущий идентификатор сеанса oracle в таблицу, чтобы триггер мог сопоставить идентификатор сеанса с именем пользователя.Проблема в том (или была), что он предполагал, что интернет-сеанс пользователя сопоставлен сеансу базы данных.Это не так, и мы используем пул подключений, поэтому идентификаторы сеанса oracle могут быть сопоставлены многим пользователям, не обязательно пользователю, вошедшему в систему в этом сеансе.Итак, я создал служебный метод на своем уровне доступа к данным, который вызывает его процедуру при каждой вставке, обновлении и удалении (гарантируя, что это происходит в одной транзакции):
/// <summary>
/// Performs an insert, update or delete against the database
/// </summary>
/// <param name="transaction"></param>
/// <param name="command">The command.</param>
/// <param name="transaction">A transaction, can be null.
/// No override provided without a transaction, to remind developer to always consider transaction for inserts, updates and deletes</param>
/// <returns>The number of rows affected by the operation</returns>
public static int InsertUpdateDelete(OracleCommand command, OracleTransaction transaction)
{
if (command == null)
throw new ArgumentNullException("command", "command is null.");
OracleConnection connection = null;
bool doCommit = false;
try
{
if (transaction == null)
{
//We always need a transaction for the audit insert
connection = GetOpenConnection();
transaction = connection.BeginTransaction();
doCommit = true;
}
command.Transaction = transaction;
command.Connection = transaction.Connection;
//TODO HttpContext requires that presentation layer is a website. So this call should NOT be in the data access layer.
string username = HttpContext.Current.User.Identity.Name;
if (!String.IsNullOrEmpty(username))
pInsertCurrentUserForAudit(username, command.Transaction);
int recordsAffected = command.ExecuteNonQuery();
if (doCommit)
transaction.Commit();
return recordsAffected;
}
finally
{
if (doCommit)
{
if (transaction != null)
transaction.Dispose();
if (connection != null)
connection.Dispose();
}
}
}
Это работает, и теперь аудит работает так, как требуется.Однако мне не нравится вызов HttpContext:
string username = HttpContext.Current.User.Identity.Name;
Это был самый быстрый способ реализации задачи, но я не думаю, что это должно быть на уровне доступа к данным.Что, если в какой-то неизвестный момент в будущем я захочу получить доступ к базе данных с помощью приложения forms?Получу ли я сообщение об ошибке при доступе к HttpContext?Есть ли лучший способ получить доступ к имени пользователя, который должным образом разделяет проблемы?Передача имени пользователя в качестве параметра для каждой вставки, обновления и удаления - это вариант, но это будет длительная задача, и мне было интересно, есть ли более элегантный способ сделать это.
Решение
То, что вы сделали, определенно не самый лучший подход (как вы изложили выше в своем вопросе) Это одна из тех вещей, которые называются сквозной проблемой - другие - это такие вещи, как ведение журнала и т.д.)
Один из используемых подходов заключается в том , чтобы передать контекстный объект это реализует функциональность для всех таких сквозных задач, так что каждый метод на каждом уровне не нужно модифицировать для передачи данных, необходимых для реализации желаемой функциональности.
В противном случае, как вы предлагаете, вам придется передавать имя пользователя на уровень данных с более высокого уровня стека в каждом методе, которому это необходимо.Если возможно, одной из альтернатив является внедрение базового класса для всех таких методов (всех методов уровня данных) и помещение этого метода в этот базовый класс...