获取数据访问层中当前用户名的最佳实践是什么?

5
我们最近为我们的数据库添加了审计功能。一位同事使用触发器实现了这个功能,并要求我在网站登录时调用一个存储过程。该存储过程将当前用户名和当前Oracle会话ID插入到一个表中,以便触发器可以将会话ID映射到用户名。问题是(或者曾经是),他假设用户的互联网会话映射到数据库会话。这并不是事实,而且我们使用连接池,因此Oracle会话ID可以映射到许多用户,而不一定是在该会话上登录的用户。
因此,我在我的数据访问层中创建了一个实用方法,在每次插入、更新和删除时调用他的存储过程(确保它在同一事务中)。
/// <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;

这是最快实现任务的方法,但我认为它不应该在数据访问层中操作。如果未来某个时候我想使用窗体应用程序访问数据库,那么当我访问HttpContext时会出现错误吗? 有没有更好的方法来获取用户名并正确分离其职责?将用户名作为参数传递到每个插入、更新和删除操作中是一种选择,但这将是一个繁琐的任务,我想知道有没有更优雅的方法。

1个回答

3
你所做的绝不是最好的方法(正如你在问题中所概述的那样),这是一种称为横切关注点的事物,其他的还有日志记录等。一种常用的方法是传递一个实现所有这些横切关注点功能的上下文对象,以便每个层中的每个方法都不必修改以传递实现所需功能所必需的数据。否则,如你所建议的,你将不得不从更高层次的堆栈中将用户名传递到数据层中的每个需要它的方法中。如果可能的话,一种替代方案是为所有这些方法(所有的数据层方法)注入一个基类,并将此方法放在该基类中...

1
另一种不需要修改整个应用程序方法签名的方法是:如果您的应用程序是标准 Web 应用程序,则一个线程在给定时间为一个用户提供服务。因此,您可以使用“全局”变量来存储“实际用户”(一个全局变量,对于访问它的每个线程都有独立的值)。DAO 层可以具有带有 ThreadLocal(Java)变量(或等效变量)的上下文。Web 层中的过滤器(因此您可以在整个应用程序中使用它)可以设置其值:DAOContext.setSuperCoolLocalVar(this_session_user)。 - helios
但是,如果你在ASP.NET中运行,就不能假设线程本地的数据只会在一个HTTP上下文中可见。 http://www.hanselman.com/blog/PermaLink.aspx?guid=320 - reustmd
据我回忆,在Windows中,无论是.Net还是经典的ASP,只有那些明确声明为线程本地存储(TLS)的变量才是局部于线程的变量,而大多数变量则不是。在基于COM的代码中,每个类实际上都是操作系统级别的窗口,因此类级别的变量位于TLS中,但在托管代码中并非如此。(ASP.Net) - Charles Bretana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接