views:

1554

answers:

5

Let's say I have a Web application implemented like a set of wizard pages to edit a complex object. Until the user clicks on the "Finish" button, the object doesn't get saved to the back-end system (a requirement), so in the meantime I have to keep the whole information about the object in some kind of a session state.

Also, some of the wizard pages have to show combo and list boxes with potentially large number of items. These items are fetched from the back-end system using a Web service.

Coincidentally, the wizard allows the user to freely jump from one wizard page to any other (using tab links on top of the form), so it's not a simple "next, next... finish" thing.

Additional constraint: the Web application runs on a Web farm and the customer is weary of using server-side session state. In the best case they want to keep the size of the session state minimal (they had problems with this in the past).

So basically there are two problems here:

  1. How/where to keep data entered by the user in the Wizard?
  2. Whether to cache the combo/list items received from the back-end and if so, where?

Options I'm considering:

  1. Storing the object in a WebForms-like ViewState (by serializing it into the HTML page). This would also include the combo box items. Obviously, there could be a problem with HTML pages becoming very large and thus Web application will be slow.

  2. Storing it into server-side session state, regardless of the customer's wishes and without knowing how the performance will be affected until it is tested on the actual Web farm (late in the project).

I cannot decide between the two. Or is there another alternative?

+3  A: 

Why cache at all? You could just have the tabbed pages where each page is a div or panel and just display the current div relating to your tab. That way you dont have to keep track and process all the inputs when the user submits the form.

That's certainly one option and a pragmatic one. I suppose the major downside is the initial page download time. This may or may not be an issue though, and on the plus side the wizard transitions will be instant.
RichardOD
OK, I forgot to mention there's a lot of server-side validation involved. Example: each time the user presses the "Next" button. So if the whole thing is implemented as a single page, then I guess I would have to resort to validation with AJAX, which complicates the design a lot in my case. But it's a valid alternative, I admit.
Igor Brejc
+2  A: 

As Daisy said, it doesn't have to be cached. You could also use hidden form fields. Because these could map to the same object on each controller action, you could progressively build the object through successive pages.

//Here's a class we're going to use
public class Person
{
  public int Age {get;set;}
  public string Name {get;set;}

  public Person()
  {
  }
}

//Here's the controller
public Controller PersonCreator
{
  public ActionResult CreatePerson()
  {
    //Posting from this page will go to SetPersonAge, as the name will be set in here.
    return View();
  }

  public ActionResult SetPersonAge(Person person)
  {
    //This should now have the name and age of the person
    return View(person);
  }
}

//Here is your SetPersonAge, which contains the name in the model already:
<%= Html.Hidden("Name", Model.Name) %>
<%Html.TextBox("Age") %>

And that's pretty much it.

Dan Atkinson
Yeap- another valid option.
RichardOD
In fact my existing design works this way. But the problem is more complex user controls, which are difficult to map in the "hidden" mode the same way as in the "visible" mode (example: a tree view filled from the back-end). Sooner or later you have to resort to using some kind of serialized view state for them if you want to avoid re-requesting the data from the back-end.
Igor Brejc
Why are they more difficult to map? Your tree view example wouldn't be a problem. And I don't agree with your assessment that you will end up needing some form of serialized viewstate. It sounds to me like you've not completely seen the benefits of MVC and are trying to find solutions that fit more closely with webforms.
Dan Atkinson
Well you'd need to be able to recreate the whole tree hierarchy from the view's hidden field(s). Another example: how would you store a select box in a hidden mode, so that you'd be able to recreate it (together with all its options) later (if you want to avoid reading the options from the backend each time the page is rendered)? It's possible, but it's not as simple as the textbox example you gave.
Igor Brejc
Why would you need to recreate the whole tree? Surely just an identifier for what is collapsed and expanded would do.
Dan Atkinson
Because I'm trying to avoid having either the tree data stored in the Session or refetching it from the back-end on each page rendering. The tree view items are not hard-coded in my code, they have to be loaded from somewhere. This is in essence what I was asking. As for the "benefits of MVC": the main issue is that the Web app I'm working on is supposed to act statefully, but the HTTP is a stateless protocol. And now I have to find the best place where to store this state. WebForms just hide these facts from you, but in the end they use either ViewState or Session state or both.
Igor Brejc
+3  A: 

Is it possible to store the wizard data in a temporary table in the database? When the user finishes the wizard the data is copied from the temporary table and deleted. The temporary table includes a timestamp to remove any old uncompleted data.

David G
Perhaps as a serialized LOB?
RichardOD
Unfortunately, the database is not an option for this.
Igor Brejc
+1  A: 

I can suggest a few more options

  1. Having the entire wizard as a single page with the tabs showing and hiding content via javascript on the client-side. This may cause the the initial page to load slower though.

  2. Caching the data at the server using the caching application block (or something similar). This will allow all the users to share a single instance of this data instead of duplicating across all sessions. Now that the data is lighter, you may be able to convince the customer to permit storing in the session.

I'm not sure what you mean by 2. I don't know why you'd want to use the caching application block in a Web app, when ASP.NET already has a perfectly decent cache. +1 for the client side wizard though.
RichardOD
@sabri, it's a Web farm and I cannot know how such caching would behave in such a configuration. It would be risky for me to assume it will be OK and I cannot try it out early enough. As for 1), please look at my answer to Daisy.
Igor Brejc
@RichardOD, My mistake. You are correct; there's no need for the caching application block and the Cache object would be much better.
A: 

I've been dealing with the same issue and, while my requirements are a little simpler (keeping state for just a few strings), my solution may work for you. I'd also be interested in hearing others thoughts on this approach.

What I ended up doing is: in the controller I just dump the data I want into the Session property of the Controller and then pull it out next time I need it. Something like this for your situation:

//Here's the controller
public Controller PersonCreator  
{  
    public ActionResult CreatePerson()  
    {    
        //get the age out of the session
        int age = (int)(Session["age"]);
        //do something with it...
        return View();  
    }  
    public ActionResult SetPersonAge(Person person)  
    {  
        //put the age in the session
        Session.Add("age", person.Age);
        return View(person);  
    }
}

The thing I like about this is I don't have to put a bunch of hidden params around on my view pages.

MikeD