【问题标题】:ASP.NET MVC: Carrying a value around from page to pageASP.NET MVC:在页面之间传递值
【发布时间】:2010-08-18 01:56:45
【问题描述】:

tldr> 选择客户后,所有其他控制器如何在不手动传递 ID 的情况下始终在该客户的“上下文”中执行其操作?

我正在尝试找出“正确”的方法来处理整个控制器(或更多)都依赖于前一个控制器的想法的情况。例如,假设您正在构建某种客户管理系统。会有各种各样的客户功能,可能位于 CustomerController。但是当您进入订单管理时,您可能希望拥有一个 OrderController。

如果您只有一个 OrderController,您的方法可能如下所示:

public ActionResult Edit(string id){...}

id 将是订单的 id,对吗?我有点迷失的地方是当我需要回到客户那里时。这就像所有发生的行为的“背景”都在客户内部。您可以通过始终将客户 ID 添加到操作 (URL) 中来做到这一点:

http://site.com/Orders/Edit/1234?customerId=abc

但是,抓住该价值并将其干扰到每个动作中似乎变得相当乏味。有 Session 之类的选项,但看起来很草率。

这样做的正确方法是什么?

【问题讨论】:

  • 为什么你认为使用 session 很草率?听起来像是一个完美的用例。
  • 我想我只是以这种方式查看会话。是的,它可以在这里工作,但是如果你尝试扩展等,就会出现问题。我通常会避开会话,就像我一直避免使用全局变量一样——它们可以工作,但它们有自己的问题。
  • 使用会话的另一个重要因素是用户不能为任何子页面添加书签,因为它们需要创建上下文。通过在 URL 中嵌入您需要的所有内容,用户可以直接返回到他们想要的任何页面。

标签: c# asp.net-mvc-2


【解决方案1】:

您可以通过在会话周围编写一个包装器来减少一般会话的草率,该包装器公开强类型属性。

//Sloppy weak typing:
int userId = (int)HttpContext.Current.Session["UserId"];

//Strongly typed goodness
int userId = SessionWrapper.UserId;

【讨论】:

  • 是的,当我在玩这条路线时,我在基本控制器中加入了几个方法来做到这一点。肯定会好一点。不过,一旦这发生在 3 个服务器的集群上,我就有一个讨厌的问题要处理。 Sql 会话存储扩展从来没有好运。
  • 我很确定这是一个已解决的问题,有一种方法可以将会话存储在 SQL Server 中,这样它就可以在集群中持久保存。也许这可能有用:support.microsoft.com/kb/317604
  • 是的,它存在,只是在重负载下很糟糕。
  • 以什么方式糟糕?是不是太慢了?不想局限于可序列化的对象?还有其他错误吗?我有兴趣听到您期望的问题。
  • 我们过去遇到的大问题是速度/争用。我说的是在重用户负载下的服务器集群。特别是随着会话的增长,它似乎对数据库服务器征税了很多。 App Fabric 会话将是一个更好的解决方案(其他人已经提到过),但这也是另一个需要解决的问题。
【解决方案2】:

如果您真的想避免将值存储到会话中,可以使用处理该值的操作过滤器。我曾经使用过这种方法,它是这样的:

  1. 创建一个新路由,其中​​包含一个占位符来表示您的值,例如客户 ID。
  2. 选择客户后,添加 id 以路由下一次 GET 或 POST 调用。
  3. 创建一个操作过滤器,从路由中读取 id 并将其添加到操作参数中,然后再执行操作 (OnActionExecuting)。当动作完成时,这个过滤器将动作参数中的 id 添加回路由数据 (OnActionExecuted)。
  4. 可以在任何地方使用这个新的操作过滤器,可能在控制器级别。

现在您的客户 ID 很容易保留在 URL 中,即使您没有手动将其添加到所有链接。如果您需要在操作方法中使用客户 ID,您只需添加一个具有正确名称的参数。即使您不将其作为参数,它也会在操作方法调用中持久化。

如果您想更进一步,在您的新操作过滤器中,您可以从数据库中获取数据并将其添加到操作参数中,而不是仅添加 id。这样您可以进一步简化您的操作方法。

【讨论】:

    【解决方案3】:

    Session 是存储此类内容的地方。

    您还可以使用可扩展的分布式缓存 (http://msdn.microsoft.com/library/cc645013.aspx)。上面链接中的示例代码

    // CacheFactory class provides methods to return cache objects
    // Create instance of CacheFactory (reads appconfig)
    DataCacheFactory fac = new DataCacheFactory();
    // Get a named cache from the factory
    DataCache catalog = fac.GetCache("catalogcache");
    //-------------------------------------------------------
    // Simple Get/Put
    catalog.Put("toy-101", new Toy("thomas", .,.));
    // From the same or a different client
    Toy toyObj = (Toy)catalog.Get("toy-101");
    // ------------------------------------------------------
    // Region based Get/Put
    catalog.CreateRegion("toyRegion", true);
    // Both toy and toyparts are put in the same region
    catalog.Put("toy-101", new Toy( .,.), "toyRegion");
    catalog.Put("toypart-100", new ToyParts(…), "toyRegion");
    Toy toyObj = (Toy)catalog.Get("toy-101", "toyRegion");
    

    【讨论】:

      【解决方案4】:

      将会话信息序列化为加密的会话 cookie 是这里的另一种解决方案。好处是能够避免消耗服务器资源并实现相同的目标:

          private void setSessionCookie() {
              HttpCookie ck = new HttpCookie(XConstants.X_SESSION_COOKIE_KEY) {
                  Expires = DateTime.Now.AddMinutes(_sessionInfo.SessionTimeoutMinutes)
              };
              DateTime now = DateTime.UtcNow;
              _sessionInfo.SessionLastValidatedAt = now;
              ck.HttpOnly = true; // server-only cookie
              ck["LastCheck"] = now.ToString(XConstants.XDATEFORMAT);
              ck["Content"] = new Cipher().Encrypt(Serializer.Serialize(_sessionInfo).OuterXml,
                  ConfigurationManager.AppSettings[XConstants.X_APPKEY_KEY]);
              System.Web.HttpContext.Current.Response.Cookies.Add(ck);
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-05
        相关资源
        最近更新 更多