【问题标题】:What is best practise for getting current username in data access layer?在数据访问层获取当前用户名的最佳实践是什么?
【发布时间】:2010-12-31 03:35:59
【问题描述】:

我们最近在我们的数据库中添加了审核功能。一位同事使用触发器实现了它,并要求我在登录网站时调用存储过程。存储过程将当前用户名和当前 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 时会出错吗? 有没有更好的方法来获取正确区分关注点的用户名?将用户名作为参数传递给每个插入、更新和删除是一种选择,但这将是一项冗长的任务,我想知道是否有更优雅的方法。

【问题讨论】:

    标签: data-access-layer audit


    【解决方案1】:

    您所做的绝对不是最好的方法,(正如您在上面的问题中概述的那样)这是被称为横切关注点的事情之一 - 其他事情是日志记录等)

    使用的一种方法是传递一个上下文对象,该对象实现了所有此类横切关注点的功能,因此不必修改每一层中的每个方法即可传递实现所需功能所需的数据。

    否则,正如您所建议的,您将不得不在每个需要它的方法中将用户名从堆栈的上层传递到数据层。如果可能,另一种方法是为所有此类方法(所有数据层方法)注入一个基类,并将此方法放入该基类中......

    【讨论】:

    • 另一种方法不修改应用范围内的方法签名:如果您的应用是标准网络应用,则线程在给定时间为一个用户提供服务乙>。因此,您可以使用“glocal”变量来存储“实际用户”(一个全局变量,每个线程访问都有一个独立的值)。 DAO 层可以有一个带有 ThreadLocal (java) 变量的上下文(或等效的)。 Web 层中的过滤器(因此您可以在应用程序范围内使用它)可以设置其值:DAOContext.setSuperCoolLocalVar(this_session_user);
    • 但是,如果您在 ASP.NET 中运行,则不能假设线程本地的数据只能在单个 http 上下文中看到。 hanselman.com/blog/PermaLink.aspx?guid=320
    • 我记得,在 Windows 中,无论是 .Net 还是经典 ASP,线程本地的唯一变量是那些显式声明为 Thread-Local-Storage (TLS) 的变量,并且大多数不是......在基于 COM 的代码中,每个类确实是操作系统级别的窗口,因此类级别的变量在 TLS 中,但在托管代码中并非如此。 (ASP.Net)
    猜你喜欢
    • 2011-02-21
    • 2015-08-15
    • 2019-01-01
    • 2011-11-22
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 1970-01-01
    • 2017-02-26
    相关资源
    最近更新 更多