【问题标题】:Passing Data from Controller to View, back to Controller, back to View将数据从控制器传递到视图,返回到控制器,返回到视图
【发布时间】:2009-11-28 22:59:46
【问题描述】:

我是 ASP.NET MVC 的新手。在使用传统的 ASP.NET 模型这么久之后,我需要一些时间来理解这个模型。

我正在通过 NerdDinner 了解事情的运作方式。

所以,我有一个对象需要通过几个视图。类似文章NerdDinner Step 6: ViewData and ViewModel

我第一次保留从Get到Post的数据,然后我把它放在TempData中并传递给另一个动作(AnotherAction)。一旦我在 Get 上获得了我的数据,我就无法在 Post 上保留它。

这是我的代码:

public class DinnerFormViewModel
{
    public Dinner Dinner { get; private set; }

    public DinnerFormViewModel(Dinner dinner)
    {
        Dinner = dinner;
    }
}

public class DinnersController : Controller
{
    public ActionResult Action()
    {
        Dinner dinner = new Dinner();
        return View(new DinnerFormViewModel(dinner));
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Action(Dinner dinner, FormCollection collection)
    {
        try
        {
            // Some code
            TempData["Dinner"] = dinner;
            return RedirectToAction("AnotherAction");
        }
        catch
        {
            return View();
        }
    }

    public ActionResult AnotherAction()
    {
        Dinner dinner = (Dinner)TempData["Dinner"]; // Got my dinner object
        return View(new DinnerFormViewModel(dinner));
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult AnotherAction(Dinner dinner, FormCollection collection)
    {
        // Lost my dinner object, dinner comes in as null
    }
}

【问题讨论】:

    标签: asp.net-mvc controller tempdata


    【解决方案1】:

    要获得您期望的格式,您可能需要在从各种视图收集信息时填充一些隐藏字段。

    此外,使用模型绑定可以使代码看起来更好,并避免在某些地方使用 TempData:

    public ActionResult Action()
    {
        Dinner dinner = new Dinner();
        return View(new DinnerFormViewModel(dinner));
    }
    
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Action(Dinner dinner)
    {
        try
        {
            return RedirectToAction("AnotherAction", dinner);
        }
        catch
        {
            return View();
        }
    }
    
    public ActionResult AnotherAction(Dinner dinner)
    {
        return View(new DinnerFormViewModel(dinner));
    
        //In this view is where you may want to populate some hidden fields with the Dinner properties that have already been entered... so when the Post action picks up the dinner object it will be fully populated
    }
    
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult AnotherAction(Dinner dinner)
    {
        //work with fully populated Dinner object
    }
    

    因此,在 AnotherAction 视图中,您会看到如下内容:

    <% using(Html.BeginForm()) { %>
    
        <%= Html.Hidden("dinnerProperty1") %>
        <%= Html.Hidden("dinnerProperty2") %>
        <%= Html.Hidden("dinnerProperty3") %>
        <%= Html.TextBox("dinnerProperty4") %>
        <%= Html.TextBox("dinnerProperty5") %>
        <%= Html.TextBox("dinnerProperty6") %>
    
    <% } %>
    

    上面的例子没有用户友好性,但你明白了。

    【讨论】:

    • 感谢您的建议。不幸的是,隐藏字段不是一种选择。有一个值我不希望用户嗅探到源代码并看到。我也许可以对此进行加密,但那将是我最后的手段。谢谢!
    【解决方案2】:

    据此blog post TempData 在设置后仅适用于 1 个单个请求。

    下面是这篇文章的引述:

    如果您设置 TempData 并且您的操作随后返回 ViewResult,那么下一个请求,无论它碰巧是什么(AJAX 请求,用户在不同选项卡中打开的另一个页面等),都会看到 TempData您设置的值,其他请求不会看到它。

    因此,鉴于我看到的代码,您可以从 AnotherAction 获取的 TempData 获取晚餐,这是您在 Action 上设置后的第一个请求。但是,查看代码并没有看到AnotherAction 的查看代码,不清楚您如何将数据传递给AnotherAction 的帖子。晚餐实例不会出现在该请求的 TempData 中,因为这是在 TempData 中设置它之后的第二个请求。如果您没有在 AntoherAction 视图上设置正确的表单标签,则框架将没有正确的表单值来实例化帖子中的晚餐对象。

    因此,您要么必须在第一次调用 AnotherAction 时使用晚餐实例重置 TempData,然后在帖子 AnotherAction 中从 TempData 中检索晚餐,或者您可以遵循 dm 的建议并使用隐藏字段在你看来。

    IMO,您应该使用 DM 的方式来执行此操作,并避免使用 TempData。

    编辑添加了在 AnotherAction 中重置 TempData 以在帖子中访问它的示例。

    型号:

      public class Dinner
      {
        public string Name{get;set;}
      }
    
      public class DinnerFormViewModel
      {
        public Dinner Dinner {get;private set;}
    
        public DinnerFormViewModel( Dinner dinner )
        {
          Dinner = dinner;
        }
      }
    

    控制器:

      public class DinnersController : Controller
      {
        public ActionResult Action()
        {
          Dinner dinner = new Dinner();
          return View( new DinnerFormViewModel( dinner ) );
        }
    
        [AcceptVerbs( HttpVerbs.Post )]
        public ActionResult Action( Dinner dinner, FormCollection collection )
        {
          try
          {
            // Some code
            TempData[ "Dinner" ] = dinner;
            return RedirectToAction( "AnotherAction" );
          }
          catch
          {
            return View();
          }
        }
    
        public ActionResult AnotherAction()
        {
          Dinner dinner = ( Dinner )TempData[ "Dinner" ]; // Got my dinner object
          TempData[ "Dinner" ] = dinner; // Reset the dinner object in temp data
          return View( new DinnerFormViewModel( dinner ) );
        }
    
        [AcceptVerbs( HttpVerbs.Post )]
        public ActionResult AnotherAction( Dinner dinnerFromPostedFormValues, FormCollection collection )
        {
          //dinnerFromPostedFormValues is null
          var dinnerFromTempData = TempData[ "Dinner" ] as Dinner; // Got my dinner object
    
          return View( "Action", new DinnerFormViewModel( dinnerFromTempData ) );
        }
      }
    

    动作视图:

    <h2>Action</h2>
    
    <% using (Html.BeginForm()) {%>
    
        <fieldset>
            <legend>Fields</legend>
            <p>
               Name: <%= Html.TextBox("Name", Model.Dinner.Name ) %>
            </p>
            <p>
                <input type="submit" value="Save" />
            </p>
        </fieldset>
    
    <% } %>
    

    另一个动作视图:

    <h2>AnotherAction</h2>
    
    <% using (Html.BeginForm()) {%>
    
      <fieldset>
          <legend>Fields</legend>
          <p>
              Name:
              <%= Html.Encode(Model.Dinner.Name) %>
          </p>
    
          <p>
              <input type="submit" value="Do Something" />
          </p>
      </fieldset>
    
    <% } %>
    

    【讨论】:

    • 在 AnotherAction 上重置 TempData 没有帮助。我的理解是 TempData 只有在 RedirectToAction 之后才有用。无论如何,我也尝试了这种方法,有和没有 RedirectToAction。当然,RedirectToAction 进入一个无限循环,调用AnotherAction 的Get 版本并在没有RedirectToAction 的情况下设置TempData 没有给我任何东西。因此,我使用的是 DinnerFormViewModel。如果您看一下代码,在我的第一个操作(获取)中,我会做同样的事情。在这里,我可以随心所欲地修改 Dinner 对象,并且我会在 Action (Post) 上获得相同的 Dinner 对象。
    • 乔恩,我添加了一个示例,用于在另一个操作中重新设置 TempData 以使其可用于回发到另一个操作。我能够成功地从 TempData 检索到晚餐对象。但是,由于我的视图没有将正确的表单变量发布到 AnotherAction,因此传递给操作的晚餐实例为空。仅供参考,我没有书呆子晚餐项目,所以我用一个 Name 属性创建了自己的晚餐类。
    • 这该死的作品亚伦,太棒了!你知道,我以为我之前已经尝试过,所以我在当前项目中按照你的步骤进行操作,但没有成功。然后我只用你的代码开始了一个新项目,它在那里工作,所以我知道我在某个地方犯了一些小错误。所以我变成了一个白痴。谢谢!
    • 我很高兴它成功了。但是,您可能需要考虑遵循 Baddie 的建议,并将晚餐实例存储在 Session State 中。据我所知,TempData 是 Session State 之上的抽象。然后你就不需要为每个请求重置 TempData 直到你不需要它。
    【解决方案3】:

    您不能将原始 C# 对象从视图传递到控制器。

    在 ASP.NET MVC 中,当操作将对象作为参数时,ASP.NET MVC 会查看所有 POST/GET 数据并查找与参数对象上的属性名称一致的值。

    public ActionResult SomeAction(Dinner myDinner)
    {
            // do stuff
    }
    

    只有当您使用与晚餐对象的属性(位置、日期等)相对应的表单字段发布到操作时,或者如果您将该信息放在 GET URL 中(Dinners/SomeAction? location=chicago&date=12/1/2009 等)

    如果您绝对不能使用隐藏字段(如 DM 建议的那样),那么 Sessions 可能是您唯一的选择。

    【讨论】:

    • 我同意巴迪的观点。 TempData 以任何方式在幕后为您使用会话状态。我会停止使用 TempData 并直接将对象存储在 Session State 中。当然,前提是你不能使用隐藏字段。
    【解决方案4】:

    您应该从存储库获取晚餐。你的动作应该是这样的:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult AnotherAction(int dinnerId, FormCollection collection)
    {
        var dinner = dinnerRepository.Get(dinnerId);
        ... do something with dinner ...
        dinnerRepository.Save(dinner);
        return RedirectToAction("AnotherAction", ... pass dinner id ...);
    }
    

    GET 操作也可以从存储库中执行,因此您只能传递 id。

    编辑

    如果要创建向导样式的页面,可以将之前输入的数据存储在 Session 对象中。

    Session['CurrentDinner'] = dinner;
    

    存储在 Session 中的数据通过请求持续存在。

    【讨论】:

    • 我没有传递 id,而是传递了整个对象。而且该对象尚未保存到数据库中,因此我无法神奇地从存储库中“查找”。
    • 那么您应该阅读有关模型活页夹的信息。您应该从 Form 值构造 Dinner 对象。为什么要多次传递晚餐而不将其存储在 DB 中?
    • 我会阅读模型活页夹。谢谢你的建议。我想从几个不同的视图收集用户的信息,当我有我需要的信息时,我会保存它。而不是进行不必要的多个数据库调用。困扰我的是,它适用于一个实例,不适用于另一个实例。这是同一个可怕的概念。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    • 2017-02-13
    • 1970-01-01
    • 2014-09-12
    • 2023-04-01
    相关资源
    最近更新 更多