I have just spent 2 hours trying to work out why when I put a string in to View.Bag/ViewData inside my Surface controller, when I try and get the string back in the view I get null.
In the end I have solved the problem by putting the string in to a session variable insted.
Would like to know though why it wasn't working, and how to fix it.
Thanks in advance.
Update: Are you posting and redirecting? When you refresh the form does it prompt you about posting again? If not, it's because you have accidentally followed the best practice of 302ing from a form post (prevents a user refreshing and reposting form data). The examples I was following for login surface controllers all used
return RedirectToCurrentUmbracoPage()
which I blindly followed. But, as the name implies that really is doing a redirect and it is really two requests! (I stubbornly had to verify in Fiddler before I believed it). ViewData and ViewBag are only good for one request--so they are fundamentally broken in a POST 302. Session is good for multiple requests which is why it worked for you. TempData will work for you too, because as it turns out, TempData is a construct that is built on top of session and was specifically designed to carry state between two posts (removed on retrieve). I read somewhere that TempData would have been better namedRedirectData
and that helped it click for me.So when you're dealing with Surface Controllers and POSTing you have three options that I know work:
return CurrentUmbracoPage();
in your form post. I just verified in Fiddler that this is exactly one request (refreshing in the browser prompts a repost warning). I also verified that ViewData works this way. But, because the surface controller is rendered as a Child Action using@Html.Action(...)
you have to useParentActionViewContext
to get at the right ViewData (my first answer which I'll leave for others that find this question).Original answer is still useful when there is no redirect involved (GET or a POST that returns
CurrentUmbracoPage()
)...In many cases you're actually making a child action. Usually you're only one level deep but if you mix macros and partials you can actually get multiple levels deep. There is a ViewData for each level and you have to walk your way up the stack with
ParentActionViewContext
to get to the topViewData
that you populated in your controller.See this comment from Shannon in answer to a question about surface controllers and viewdata (Shannon is a core contributor on the HQ team and has a lot of great content out there). Quoting here: