views:

811

answers:

3

The following setup kind of works, but it's not the proper use of TempData, and it falls down after the first use. I suspect I'm supposed to be using ViewData, but I'm looking for some guidance to do it correctly.

In my Home controller, I have an Index action that shows which members need to have letters created:

Function Index() As ActionResult
    Dim members = GetMembersFromSomeLongRunningQuery()
    TempData("members") = members
    Return View(members)
End Function

On the Index view, I've got a link to create the letters:

<%=Html.ActionLink("Create Letters", "CreateLetters", "Home")%>

Back in the Home controller, I've got a CreateLetters function to handle this:

Function CreateLetters() As ActionResult
    Dim members = TempData("members")
    For Each member In members
        '[modify member data regarding the letter]
    Next
    TempData("members") = members
    Return RedirectToAction("Letter", "Home")
End Function

Finally, there is a Letter view which creates the actual letters. It's ContentType is "application/msword", so the data actually comes up in Word instead of the browser.

Like I said, this works great the first time through. However, since the letters come up in Word, when the user comes back to the browser, they are still sitting on the screen with the "Create Letters" link. If they try to hit it again (i.e. paper jam after the first attempt), they get an error in CreateLetters because TempData("members") is now empty. I know this is how TempData works (single shot requests), which is why I'm trying to move away from it.

How would this be refactored to use ViewData? Alternatively, how could I simultaneously display the Letter page (i.e. Word document) and redirect the app back to the Index?

A: 

Could you just stuff this in an application variable? Application("members")?

Slee
Please try not to use application variables except in the most extreme conditions and only after visiting a therapist to ensure that you are not reliving your Classic ASP days.
Neal
A: 

I would simply retrieve the object again on the other view but why not cache the object when it is first retrieved

Function Index() As ActionResult
    Dim members = GetMembersFromSomeLongRunningQuery()

// Pseudo code (am a C# man sorry)
    Cache.Remove("MembersItem")
    Cache.Add(members,"MembersItem", CacheExpiry....)

    TempData("members") = members
    Return View(members)
End Function


Function CreateLetters() As ActionResult
    Dim members = Cache.Get("MembersItem")

    For Each member In members
        '[modify member data regarding the letter]
    Next
    TempData("members") = members
    Return RedirectToAction("Letter", "Home")
End Function

You should also consider caching the results of the query for a specified time or make it dependent on an update. You could also use the session object if it is user specific data.

Richard
+1  A: 

TempData is designed for keeping data across a redirect and ViewData is designed for passing data to the view. Neither of these are really suitable to what you are trying to achieve although TempData can be made to work in this way by creating an extension method that you can call in the Letter action ( as well as replacing the TempData("members") = members line in CreateLetters )

using System.Web.Mvc;

namespace GoFetchV2.ExtensionMethods
{
    public static class TempDataExtensions
    {
        public static void Keep( this TempDataDictionary tempData, string key )
        {
            var value = tempData[ key ];
            tempData[ key ] = value;
        }
    }
}

Used like ...

Function CreateLetters() As ActionResult
    Dim members = TempData("members")
    ' Do something with members but want to keep it for the next action '
    TempData.Keep("members")
    Return RedirectToAction("Letter", "Home")
End Function

Function Letter() As ActionResult
    Dim members = TempData("members")
    ' Do something with members but want to keep it for the next action '
    TempData.Keep("members")
    ' Return the msword binary data '
End Function
Neal