The Correct Separation Of Concerns
I have just read this post from Hammett, talking about the difference between separating business logic and presentation logic vs. separating presentation and presentation logic. This comment has caught my eye, Nicholas Piasecki says:
To me, this discussion all boils down to one thing: the foreach loop. Let’s say you want to display a table of sales reports, but after every tenth row, you want to print out an extra row that displays a running total of sales to that point. And you want negative numbers to appear in red, positive numbers to appear in green, and zeros to appear in black. In MonoRail, this is easy; with WebForm’s declarative syntax, just shoot yourself in the face right now. Most solutions I’ve seen end up doing lots of manipulation in the code-behind and then slamming it into a Literal or something, which to me defeats the purpose of the code separation.
And that, to me, is the essence of why I dislike WebForms, something like this is possible, but very hard to do. In my current project, we have used GridViews only in the admin module, and we have regretted that as well.
Comments
Quoting the qoute:
"In MonoRail, this is easy;"
Actually, it's as easy in php, asp, RHTML, (N)Velocity, and many other web environments, that are not over-engineered.
But I'm with MonoRail too :)
"In MonoRail, this is easy"
Can you please show an example of this. I work mostly with web forms and deal with this problem all the time. I would love to see a MonoRail example of this to help push me towards MonoRail development ;-)
Scott
Stupid question, but why is this so hard to do in WebForms? I don't know if the code meets whatever level of perfection, but at www.asp.net, Scott Mitchell's working with data tutorial series has an example of how to do something similar (he is putting a separator row in between category types, but it isn't much of a leap to go from that to the example given above).
Overriding the Render method doesn't seem that big of a deal to me.
"Can you please show an example of this. I work mostly with web forms and deal with this problem all the time. I would love to see a MonoRail example of this to help push me towards MonoRail development ;-)"
Haven't bothered checking syntax or anything but here goes:
set ($i = 0)
set ($running_total = 0)
foreach ($report in $reports)
#each
#between
end
Please compare the maintainability or the proposed solutions. The amount of intimate knowledge you have to have for a simple scenario is way too much.
Yes, but if you look at flukus' proposed code (and he posted it off the top of his head), you are basically overriding the Render method of a WebForm control.
Each requires 'intimite knowledge' does it not? With WebForms, I can override Render when I need it, with MonoRail, I have to create the Rendor method for every control, since you lose the availability of standard WebForm usable controls.
If I'm a corporation, do I want to have to re-create the GridView control every time I need one? Using PHP like syntax writing out <tr> tags? As opposed to learning standard code methods of how to manipulate a GridView?
Is there a standard set of Monorail controls available? That would be of great help.
There is the ViewComponents project, which is starting to accumulate useful stuff.
I think that you are missing the point, you are outputting HTML, you WANT to have the <tr> there.
About complexity and intimate details, here is Steve's tutorial about how to add a footer summary row (something that people are able to do from the top of their head in that tiny comment box:
http://www.asp.net/Learn/DataAccess/tutorial15vb.aspx?tabid=63
I couldn't find the tutorial that you are talking about, but from experience with such tasks in the past, they tend to be fragile, and highly dependent on reflector knowledge.
That's an interesting one, but not the one I was thinking of.
You don't need reflector knowledge to do what the example in your post was requesting. Not that I've ever seen.
And, I'm not missing the point, I DO NOT WANT TO HAVE TO HAND-WRITE HTML everytime I want to have a control on a page. That seems to me to be what Monorail requires and which you seem to be acknowledging.
If to use Monorail I have to give up every single control in the Asp.Net toolbox and every single 3rd party control that exists, just so I can write PHP syntax to emit <tr> tags in the proper order....I can't see any corporation that would want to do it.
As a control freak (pun intended), sure maybe there is some pull to that. But to drive a company/corporation to have to re-write the entire universe of controls using PHP syntax (which I'm guessing has limited Intellisense support) because of the difficulties of managing the WebForms pipeline?
Not a chance. The supposedly terrible example about putting in separator rows isn't that hard to do using WebForms, and dont' require reflector knowledge, that I can see.
If you need to rely on VS tools to output the equivelent code for you then monorail is obviously not for you. A few points though:
"just so I can write PHP syntax to emit <tr> tags in the proper order"
Your looking at this upside down. nVelocity files are pretty much just html files with a few control structures, much like aspx pages.
"I DO NOT WANT TO HAVE TO HAND-WRITE HTML everytime I want to have a control on a page"
As with WebForms, you don't have to 99% of the time. There are pre built controls that do it for you, just like the aspx tags! Or do you use the designer for everything?
"But to drive a company/corporation to have to re-write the entire universe of controls using PHP syntax "
It's the web, surely there can't be that many non standard controls you use frequently?
"PHP syntax (which I'm guessing has limited Intellisense support)"
You guessed correctly. But this is view code, html with a few control structures. If you need intellisense for a few ifs and for loops then god help you.
"But to drive a company/corporation"
The hardest part of any non trivial site is managing changes and keeping everything simple and maintainable. When your goal is to output html nothing makes this simpler than writing the original in html.
My code would have survived, unchanged from the .net beta releases (assuming nVelocity etc. where around then). Microsoft likes to change their gridviews with every release. And now there are rumours of MS releasing there own MVC framework, so it could change again one day.
"The supposedly terrible example about putting in separator rows isn't that hard to do using WebForms, and dont' require reflector knowledge, that I can see."
Care to point to a tutorial thats simpler than the above code I provided?
Alright, I had to see how I'd implement this on a Monday night and I think I might have to yield. My ASP.Net code exceed 300 lines with a couple of classes and lots of methods to get things readable. I used three dynamically created GridView controls, two of them inside a Repeater control and the third was for the grand total. Although, I don't think the code for the phoney business logic to get 25 records nor the JavaScript for the flying piggie should count towards the total number of lines.
The following two links show the report in action and the source code respectively. On the other hand, mine is the only 100% functional entry... i'd be interested in seeing the full code for an equivalent page in other languages. This kind of tinkering and procrastination is fun to do when you ought to be finishing the revisions on a book. Thanks Ayende!
http://www.andrewdothay.net/code/report.aspx
http://www.andrewdothay.net/code/report.aspx.html
@jdn
You'd probably be surprised to see how many "Big Corporate" websites are not based on WebForms.
Actually, 99% of them.
And not even Apple/Sun/IBM, etc, but even Microsoft sites aren't exactly WebForms based (is there a 'master_uc1_uc2_txtLabel' somewhere?)
But is the gridview the right fit? Many times I think people end up using Gridviews or whatever is on the toolbar because that's what is there. Gridview then has to be override to function differently to what is required which causes a requirement for a deeper level of knowledge of the page lifecycle and underlying gridview functionality.
In the question asked, if you just wanted a table to render with a total line couldn't you just do this in webforms? (going to similar to what flukus posted)
private void FancyTable()
I am unsure if the comment in question is also assuming databinding is taking place or added functionality not mentioned, which would a whole different kettle of fish.
With both the asp.net solutions posted (to be fair lets look at jaffins becuase nVelocity doesn't do data binding as such) you run into the problem ayende mentioned in the original post.
You have seperated your presentation logic from your presentation logic.
Both solutions are implimented in c# instead of the standard aspx way. This creates 2 areas in 2 languages that need maintaining.
"As with WebForms, you don't have to 99% of the time. There are pre built controls that do it for you, just like the aspx tags!"
Which are these? I'd certainly be interested in seeing the pre-built control toolkit to use with Monorail.
That would certainly ease the use of it. Almost every example I've seen makes it seem as if every control is like a Repeater. If that isn't the case, I'd be very happy to be wrong.
"is there a 'master_uc1_uc2_txtLabel' somewhere?"
That looks like code that would be emitted by a custom user control.
I like jafin's example. That it is done in C# instead of the 'standard' aspx way isn't much of a burden.
Most of the difference is that you rarely need custom controls in MR, because you work directly with the HTML, there are stuff like charts, grid, etc, you can find them in the view component conrtib repository.
" That it is done in C# instead of the 'standard' aspx way isn't much of a burden" - You may think so, but I disagree, it require a huge context switch to move from code generating code, because you need to see the end result while you write the code that generate it.
Templating langagues are much easier.
In most cases my form would bind on to a list of reporting objects (report + running total) objects. This way I can test them independently of the form.
But in this example, if you think of the Running Total as a field of the form (yes really it is a field of just the part that uses it) then you can use a repeater in pretty standard style:
<asp:Repeater runat="server" DataSource="<%# Reports %>">
</asp:Repeater>
Having said all of that, I don't like the WebForms abstraction, I just don't think this is an ugly enough case to show why!
I usually agree on some level regarding WebForms complexity, but this post bothered me.
Quote: "something like this is possible, but very hard to do"
How hard is "very"? I decided to use the ObjectDataSource as well because i know how much Oren hates it :-)
I wrote a sample and pasted it to pastebin.com...
WebForms Markup: (GridView, ODS, and some style rules)
http://develocity.pastebin.com/f2f8f6042
WebForms CodeBehind: (ItemDataBound event handler, ReportItemType class, and SelectMethod for ODS)
http://develocity.pastebin.com/f75ebbcdc
Robert,
Very good, certainly better than what I would come up with.
But now you have two places where you mess with the presentation, two places that you need to keep in sync, in two drastically different environments.
"it require a huge context switch to move from code generating code, because you need to see the end result while you write the code that generate it."
Yeah, I don't see this. Plus, if you are writing per flukus' example, I don't see how you can see the end result any better than doing it the other way. You still have to see the end result page (since you can easily fat finger a <td> tag in all that stuff).
Perhaps you get used to it after a while.
"you rarely need custom controls in MR, because you work directly with the HTML"
That makes my teeth hurt.
You have helpers for the standard fields like:
$FormHelper.TextField("contact.name")
and if you have a datasource to select from etc:
$FormHelper.Select("oneprime", $primenumbers)
all the form helpers are here:
http://www.castleproject.org/monorail/documentation/v1rc2/helpers/form/index.html
What you won't see is things like panels and layout grids, thats all done in standard html/css.
One way to look at it is with webforms and its controls you have to make your ideas fit the controls. That means besides the very simple cases, you need to understand the way the controls work.
With the templated system, you make the template fit your ideas. You can start from the simple case and make it evolve gradually to more complicated by adding to the template.
An example, you drop a menu control on a form this is what it rendres to:
With the template system the simple menu renders to:
Documents (1 file) 369 bytes
Now you can implement the same thing in webforms without the menu control, but when you gradually make your menu more complex, you go back to the control.
Now you realize you want the menu to render divs instead of tables, you need a menu adapter (More complexity to render something simpler)
Now you want to render an Ajax autocomplete control one of the menu item, then you need to customize the menu control (Not trivial)
The biggest problem I find with Monorail is that the community is not big enough to produce enough code, templates, starter kits...
If I could find a project like Dasblog or Blogengine.net or the Commerce starter kit implemented in Monorail, I would rather use that than the webform projects as it would be easier to customize.
Proof is this blog is powered by subtext...
Neat! A comment and an example that I half-heartedly made up while nursing a beer after work has caused a small scale nuclear war!
K. Scott Allen has a good counterexample over at his blog. I'll quietly exit right with these points:
Most ASP.NET examples use databinding and declarative markup, so I think a lot of people (okay, maybe just me) think that it's the only way to go. TRepeaters are red herrings in some cases, so to speak. This probably happens when people like me come to ASP.NET from a different Web development background.
Being able to mix the procedural solution (such as Mr. Allen's) and the declarative solution (with GridViews and hand-waving in the code-behind) is very strange and confusing to me (I know when THIS runs but when does THAT run?). This is just a personal opinion.
I went through a stage of thinking Web controls were totally awesome ("Ohmygod I just dragged into onto the designer and I HAVE A FORUM! WAZOW!!") to getting over them. I'll admit that the projects I've worked on are downright miniscule compared to the ones y'all have worked, but I've found that whenever I've built or used a reusable HTML control, it's just not quite right for the context I need to use it in. I need to modify it somehow. And when most controls are just HTML, a few inputs or a table, I'd rather just write custom HTML once and slap it in a ViewComponent that's tailored to my site than figure out what a pre-built control is doing and hack that.
Again, personal opinion, but the idea of having a control "generate" HTML scares the patootie out of me. Some of the default WebForms controls (Menu, Login thingie) generate some insane tables, and only by that weird CSS Control Adapter band-aid can you get around that. On a public-facing Web site, I think you should be making conscious decisions for the HTML that is being output. Especially for semantic, accessible markup, or for gracefully using some third-party JavaScript library. But for a corporate intranet? Drag and drop all you want, I think that's fine.
HTML + code intermingling is fine as long as the code is presentation code, and all of the database wrangling is done somewhere else. The whole declarative thing (to me, at least) is just obfuscating / hiding this HTML + code intermingling, creating an artificial divide betwen the aspx and the code behind.
For some reason this blog thinks ".name" isn't a valid TLD. Actually I see this a lot on the Interwests, a lot of regular expressions cap at 3 letters or something.
Maybe one size just doesn't fit all and both are good solutions =)
Again, just my two cents! =)
J,
Different renderring requirements are handled by different views for the same view component, or different views for the same action.
Um, no. I am using subtext because it was simple to install, period. There are a couple of Blog engines running MR, but I don't want to deal with my blog engine, I have taken only a cursory look at SubText code, and I have little intention to work with the code in the future
The example by Flukus can be done in ASP.NET too, but guess what? This is considered bad, mixing code with markup. It's one of the reasons why we left classic ASP for ASP.NET.
But I agree that if the GridView had support for some kind of intermediate row display, including intelligent binding, that would be nice. But the thing to ask is: should a generic control like GridView that already has many options, support every scenario you can throw at it?
I think the ASP.NET team decided that this problem was specific enough for the developer to handle in the vent of to create a custom control if it is needed in more than one page. At that point you have to realize that custom control can be difficult to create, certainly more difficult than writing the code in between the markup and be done with it.
Oh and by the way, Josh has it covered with sort of a halfway solution using the repeater and some code (if/else). That should satisfy most of you right?
The repeater is a nice control, it doesn't generate any markup other than what you say it should. In the next version of ASP.NET there's going to be a listview control with 100% custom rendering.
In this thread there's some BS being thrown, like MS changing the gridview with a new release? They didn't, it was new to ASP.NET 2.0, the previous control to use was the DataGrid (IIRC).
Also, Microsoft is officially working on an MVC framework that will support all WebControls/ASPX out there.
Mile, it is literally impossible to change the GridView, because the way it is built, extending it require deep knowledge of its internals, which means that any change can significantly break backward computability.
I would say that this is the main reason each framework version comes with a new grid control.
I would wait for that, but I doubt that we will have something that is truly interesting as long as the ASPX life cycle is there, it interfere too much with the proper control of the
Ayende, I don't see your point when you are saying "But now you have two places where you mess with the presentation, two places that you need to keep in sync, in two drastically different environments. ".
According to my point of view it's a god gift to have those two places. The ASPx view with markups and css, is the responsibility of a pure graphic artist someone who will be able to transform you a blank page in something more beautiful. Don't ask him/her to perform a single "if" or "for" this is not his/her domain.
The code behind page will be the place where you'll be able to find your presentation logic without being bored with the colors and the markup’s closing. Something with type check, the place to be for a UI developer without any artistic talent.
With a simple NVelocity template mixing both views, the two members of a team as described above will have to cooperate more deeply for a small change.
I'm in charge of a team and we've to maintain both styles of presentation and when we receive upgrades to perform on the UI it's always easier to do them when we've the ASPx template and the code-behind file.
Seddryck ,
What you describe is not something that I see happening.
There is a very tight correlation between the code behind and the ASPX, something as simple as changing the columns order can break things horribly.
Beyond that, today's HTML is not static, you have to use JS to do all the dynamic stuff, which means that your designer can no longer live in ignorance from the dangers of "if" & "for".
I agree that the two files are in correlation but not as much as you describe. If you ask to a team working with WebForms to permute two columns, the designer will make the change in two seconds, and the developper will adapt a simple constant. Surely not the end-of-the-world.
I'd agree with you that when you change the type of a control in a templatedcolumn, it can be really boring, because the casting in the code-behind will probably not succeed.You just need two tasks, in two different files and coordinate those tasks. But if you think abuot twice it sounds logical, you change the layout of your screen but also the presentation logic.
JS is something for a third man or profile and should be done in a third file and surely not mixed with the html. I don't really see your point is it really the same person in your team that design the webpage and develop the JS stuffs, is it really the same profile, the same spirit in those tasks?
It's just a question of point of view, according to yours "presentation" is a simple block where html, javascript & enhancements and presentation logic are placed together and managed by one man. According to mine, html, javascript and conditional logic are three different area which solve the same concern but on different axes, interactions between them exist but I see more benefits to have them in different files than having a single confusing file..
I don't say that they are not linked at some place and that a change in the name of a css class will have no impact on code-behind and js stuffs. but just that having those different files helps to keep the three area understandable.
I'd be interested to know which developers who eagerly defend the Web Forms model here have
1) worked in frameworks other than ASP.NET, and
2) whether they started in ASP.NET to do web development
Not to make a point or anything, but just curious.
My personal experience has been simply that while these frameworks were created to bring business value to our clients,WebForms perhaps fulfilled this only until an application got mildly complex, at which point RoR and Monorail let me deliver faster and more maintainable code.
Just my 2000 pesos.
Mike,
I worked with ASP first, then ASP.NET, about 4 years each. I've never worked with Ruby (and I won't until I see a more convincing example). I guess it's just a matter of taste, but I can't see how the spaghetti code can ever be maintainable. I noticed that, although it was really hard to switch, now I make applications a zillion times faster and don't have much problems maintaining them. I'm not a big fan of Microsoft patterns, but fortunately I don't have to use everything they suggest (not force). I don't use datasets. I don't put much code in the code-behind. I use drag-n-drop datagrids in the admin interface, where I don't need much fancy stuff, but I use DataLists and custom controls in the common interface. So, I have choice, and I can do different parts of my application differently, depending on my requirements. I don' feel like I'm forced to do anything. If I can't use a built-in control, I can always switch to the type of code that flukus provided, or I can find a zillion other ways. Note that the Webforms guys provided several solutions to the problem, while the Ruby guys came up only with one.
What I'm particularly happy about is that I don't have to watch my tags correctly closed. Even if I edit my page with a notepad and make a mistake, the engine immediately tells me about it. Compare it to forgetting to close a div, or misspelling it. Now, a page like this has how many, 10 lists, all inside divs and spans. How do you maintain this? How does a designer make a change (good point, Seddryck!)?
Ayende,
From your comments it looks like your idea of maintainable code is everything in one big file. I, however, don't think you are going to put the report class in the same file.
For me, a real separation of concerns would be:
First, don't bother with your HTML. It can't be a business requirement that this stuff should be a table. It just has to look like a table.
Second, perhaps a loop could qualify as presentation logic, but calculation of a running total can't for sure. So, I'd prepare a list that contains both data and running totals and put the preparation code into the Presenter. This list should contain the information about the presentation style, but not the name of the css class, rather some enum values. The markup could be a DataList, for example, or a DataGrid. One can easily changed for another (that's maintainability). I prefer DataLists -- they can use any item template, and with a single parameter change they switch from tables to spans.
Last, the codebehind just assigns the data source for the grid. Code available on demand.
I really think that Ruby is great. I think that it deserves more than such discussions. Instead of highlighting Ruby's features, people put much energy into trying to prove that Webforms are bad, just because they are different.
"Ayende,
From your comments it looks like your idea of maintainable code is everything in one big file. I, however, don't think you are going to put the report class in the same file."
Castle/ROR are designed to discourage this, Much more so than asp.net.
"Second, perhaps a loop could qualify as presentation logic, but calculation of a running total can't for sure."
I disagree. It's a value that has no meaning anywhere outside of the view, so I keep it in the view.
I'd also like to know how flexible master pages are?
I'm working on a project at the moment that runs a number of sites of the same codebase. >90% of the code is the same across sites, so it makes no sense to have multiple projects.
I have a before filter the sets the correct layout file (master page) for the request based on the url. I also allow individual sites to "override" individual views(pages) if they like.
I do this with ~10 lines of code in monorail, how hard would it be with asps master pages aprpoach?
flukus, to do the same thing with MasterPages requres that the MasterPageFile property be set during the PreInit event.
There are 3 ways to do this with MasterPages.
1) handle the event on each page
2) create a subclass of Page that handles the event; all other pages inherit the new subclass
3) create an HttpModule that attaches to the PreInit event of each page during the HttpApplication.PreRequestHandlerExecute event.
I prefer #3 if the technique will be used on multiple pages. It's more than ~10 loc, but...
HttpModule (and custom Attribute if needed)
http://develocity.pastebin.com/f3a768196
web.config
http://develocity.pastebin.com/f765b453e
Oh, and please ignore my comments about Ruby. Should never ever post at 3AM again.
Still, I think it's a matter of personal preference, and not worth spending so much time on inventing various solutions. Turns out that SoC is pretty subjective, and so is maintainability. For some people the less lines of code and files to maintain, the better, others (like me) prefer lots of small reusable classes. I won't return to the spaghetti stuff no matter how bad Microsoft is, you won't start using drag'n'drop too, why all this heat?
Consider a real-world scenairo I came across...
Requirement: I needed a two-row header, where a header in the first line spans multiple columns. This is simple in HTML.
In the WebForms version of the app, this was near impossible. Go ahead, try.
In the MonoRail version of the app, I wrote the HTML the way I wanted it, and I was done.
How did I get automatic column sorting in MonoRail? I used TableKit -- which is 100% client-side and whose integration is embarassingly simple.
This situation finally sold me on MonoRail. I'm a web developer -- I can and want to write HTML. MonoRail lets me do this.
If you want drag-and-drop, canned functionality, and can accept its limitations, WebForms is fine. That's not what I want, so MonoRail is my choice.
Spanning a header over multiple columns is relatively easy. Check here:
http://www.asp.net/learn/dataaccess/tutorial29cs.aspx?tabid=63
Or here:
http://blogs.msdn.com/mattdotson/articles/541795.aspx
From Hammett's post about this topic, I posted a long-ish reply about all of this stuff:
http://www.blogcoward.com/archive/2007/07/26/49.aspx
Cheers
Have you seen this post:
http://odetocode.com/Blogs/scott/archive/2007/07/24/11135.aspx
?
Julian, yes, I did.
I even responded here:
http://ayende.com/Blog/archive/2007/07/28/Presentation-Logic-amp-Semi-Integration-Tests.aspx
Oren:
In that post you are replying to Scott McMaster, the link I gave you is to a Scott Mitchell post.
Comment preview