【问题标题】:How will a static delegate access non-static, server-side, session-based information?静态委托如何访问非静态的、服务器端的、基于会话的信息?
【发布时间】:2014-04-27 04:30:03
【问题描述】:

对不起标题。这是相当复杂的。我会尽量用一种简化、抽象的方式来概括它。

在 Web 表单应用程序中,将在两种不同的上下文中使用 DAL:

  1. 来自代码隐藏
  2. 来自 Web API 控制器

在每次调用中,DAL 都需要知道当前的用户 ID,以便它可以针对数据库事务存储此 ID。我在 DAL 中添加了一个 Func 类型的静态属性,这样当使用 DAL 时,可以为它提供一个方法(通过 callng 代码),允许它获取用户 ID。

从代码隐藏进行的调用将提供从会话中获取用户 ID 的 GetUserId 方法,而 Web API 控制器调用(无状态)将用户 ID 作为参数并能够返回该值.

我担心的是,Web 环境中的静态数据是跨会话共享的。所以..

  1. 当用户 A 运行设置 GetUserId 方法以返回 Session["UserId"] 的会话,而用户 B 运行同时执行相同操作的会话时,每个事务是否会收到正确的用户 ID?我在想,因为 HttpContext.Current 本身就是静态的,所以应该没问题。
  2. 当用户A通过Web API控制器调用并设置GetUserId方法返回参数值(假设id为1),然后用户B同时进行同样的调用,但用户id参数为2... 一个会取代另一个,还是会保持不同?我想他们可能会发生冲突。

如果他们会发生冲突,那么我会找到另一种方法来做到这一点,但我想避免将用户 ID 传递给每个对象 Save()。

解决方案

在 Steven 的回答的提示下,我玩了 CallContext 只是为了向自己证明它会针对特定线程保留上下文,尽管是通过静态调用。下面是一个非常简单的重叠线程测试。接下来我在Web API环境下试试,应该是一样的。

class Program
{
    static void Main(string[] args)
    {
        for (var i = 0; i < 10; i++)
            StartTestThread(i);

        Console.ReadKey();
    }

    private static void StartTestThread(int i)
    {
        Thread.Sleep(2000);

        ThreadPool.QueueUserWorkItem(delegate {

            Console.WriteLine(string.Format("[{0}] Setting context...", i));
            SetCallContext(i);
            Console.WriteLine(string.Format("[{0}] Waiting...", i));
            Thread.Sleep(5000);
            Console.WriteLine(string.Format("[{0}] Context is {1}", i, GetCallContext()));
        });
    }

    private static void SetCallContext(int i)
    {
        CallContext.LogicalSetData("Test", i);
    }

    private static int GetCallContext()
    {
        return (int) CallContext.LogicalGetData("Test");
    }
}

【问题讨论】:

  • 我认为您应该完全回避这个问题,并将一个值传递给您的 DAL 构造函数或方法调用。我明白你想要达到的目标,我只是认为这不值得。
  • 顺便说一句:HttpContext.Current 由Global.asax 填充每个请求,会话也是如此。

标签: c# asp.net session data-access-layer


【解决方案1】:

HttpContext.Current.Session 读取显然是安全的,因为HttpContext.Current 为每个请求返回不同的HttpContext 实例。但是由于您的Func 字段是静态的,替换Func 当然会使您的代码失败,因为每个应用程序域只有一个静态字段实例。所以你应该采取不同的方法。

例如,与其替换 Func,不如将其初始化一次并让它处理 Web API 案例。例如:

GetUserId = () =>
{
    var context = HttpContext.Current;

    // MVC / Web Forms
    if (context.Session != null && context.Session["UserId"] != null) {
        return (int)context.Session["UserId"];
    }

    // Web API
    return (int)CallContext.LogicalGetData("__userId");
};

public static void RegisterWebApiUserId(int userId) {
    CallContext.LogicalSetData("__userId", userId);
}

【讨论】:

  • 所以每个用户会话在 CallContext 中都有自己的 GUID 键,这导致了用户的 id。但是 DAL 对象如何获取该 GUID?我以前没有使用过 CallContext,所以请原谅我的慢。还是只是 DAL 的每个实例化为该实例生成一个密钥,然后静态方法.. 等等.. 静态方法将无法看到非静态密钥,是吗?
  • @MattWinward:不,您在应用程序域中生成一次密钥。我更新了我的问题以使其更加明显。
  • 啊,好吧。所以键可以只是一个静态值(示例中的“__userId”),CallContext 为每个“调用”存储一个值,有效地提供与 Session 相同的功能,但对于 Web API 调用?
  • @MattWinward:“调用”将具有​​单个 Web API 请求的生命周期,流与异步方法调用,甚至流与请求跨度的并行任务。
  • 谢谢。这太棒了。我会试一试,看看会发生什么!
猜你喜欢
  • 2013-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-31
  • 1970-01-01
  • 2014-12-17
  • 2018-08-18
相关资源
最近更新 更多