By Ramon Leon - 26 September 2006 under Seaside
When I first started using Seaside, it took me a while to shake out a few misconceptions that were holding me back. Many of the things I'd learned to do in .Net were no longer necessary, Seaside was different, and I had to shake some old habits before I finally grokked it. I'll use this blog to try to explain a few of them, as time permits .
First thing; State. .Net pages are stateful, but in a different manner than Seaside. .Net, in an effort to emulate desktop development, created the concept of viewstate, which was in essence nothing more than a hash table that was automatically serialized and deserialized into a hidden form element, something developers used to do by hand.
Server controls, stateful UI widgets, were then able to save and restore their state into this hidden field on each postback, giving the illusion that a page maintains its state between postback, and removing from the programmer, the hassle of thinking about request.form and repopulating form fields on each postback. This was often done by hand in classic ASP by having the value of a field be equal to its request.form value, which is where the concept of a post back originated.
The viewstate abstraction, while useful, leaked, as all abstractions do. .Net pages are classes, and like any class, can have member variables. However, only "server controls" remembered their state, and only because their authors utilized viewstate to do so. One cannot simply assign a member variable and expect it to still be there after the next request. For value objects, one could simply store the object directly in the viewstate hash, but for reference objects, developers got in the habit of storing id's to the objects they needed, in the viewstate, and then on each postback, an accessor would fetch the object using the id, allowing the illusion of a stateful page.
Storing id's and passing id's around in query strings and cookies, isn't very object oriented. Problem is, the developer had to wire this plumbing up manually, each time he needed it, it was more of a design pattern than a framework.
.Net, like Seaside, has sessions, and can store objects in that session, but unlike Seaside, each time a page is viewed in .Net, a new instance of it is created, and its state is restored from the previous instances viewstate. Pages never lived longer than the current request. All of these issues, and hacks, are a symptom of a core problem, pages aren't stateful. No matter how much you want to pretend the web is stateless, it really isn't. If pages aren't stateful, you have to manually fake it.
This is where Seaside's first big conceptual difference lies, Seaside components, the equivalent of .Net pages, are not only stateful, but can have a lifetime equal to that of the current session. Each time you view a page from Seaside, you're viewing the exact same instance that was initially created when you first entered that page. This has quite a few advantages.
Instance variables are not lost between postbacks. For example, if you set an instance variable equal to a customer object, that customer object will stay there throughout the entire user session, you need not ever fetch it again, or hold onto its id, or even think about state. This is exactly how you'd program a standard WinForm application.
Seaside components are also objects themselves, when you create a component, you create it, not the framework. This means you can use constructors, and pass any necessary state directly to the component. In .Net, this isn't the case, the framework creates the page for you, and you read in necessary state from the query string.
Let's say your going to edit a customer object, and you're on the search page, in .Net, you'd write something like the following in a databinding method for a link server control to setup a url to link to the detail page...
aLink.Url += customer.Id;
or a more older style redirect to the detail page...
Response.Redirect("CustomerDetail.aspx?id=" + customer.Id);
and on the detail page on each postback of that page, you'd have to re-fetch the customer with something like this...
Customer customer = Customer.Find(Request.QueryString["id"]);
But you only have to do this, because pages aren't real objects, and can't remember their own state. In Seaside, you don't redirect to a new page with an id in the query string (you could, but it'd be stupid), rather you write the link like this...
html anchor callback: [self call: (CustomerDetail for: customer)]; text: 'edit'.
When the link is pressed, a detail control is created, and simply handed the customer object directly via its constructor. The called component simply replaces the calling component in the UI. The calling component, though not visible, stays alive waiting for the called component to return. Simple, elegant, and exactly what you'd do if you were writing a WinForm application for the desktop. What Seaside does for you, is take the code in the callback block, and create a continuation, a unique link pointing to it, and writes the URL to the anchor tag for you. It simply automates what you have to do by hand in a .Net web application.
Understand this one thing, will help you tremendously when attempting to write a web application in Seaside. I wasted a lot of time trying to figure out how to set and read query strings and form posts and assign urls to anchors, because that's what I was accustomed to doing. If you want to try Seaside, you've got to drink the cool-aid deeply, accept its idioms, and get on with getting the job done. Seaside is "truly" object oriented, .Net simply pretends to be, and then punishes you when you actually try and program in an OO fashion.