This post originated from an RSS feed registered with .NET Buzz
by Peter van Ooijen.
Original Post: RADical ASP.NET in VS 2005 without messing it up right from the start
Feed Title: Peter's Gekko
Feed URL: /error.htm?aspxerrorpath=/blogs/peter.van.ooijen/rss.aspx
Feed Description: My weblog cotains tips tricks and opinions on ASP.NET, tablet PC's and tech in general.
The term RAD is more or less a hot topic on Codebetter. It started with a "RAD kills" post by Jeffrey which lead many people to chime in. As I almost glorified RAD it's time to write something more elaborate on how I think you can work with RAD without making a mess out of it. This will dance around the objectdatasource and the dataset. Two components who are surrounded by a lot of RAD-wizardery. I will not treat those themselves, most of it can speak for itself, but will try to place them in a broader application wide context.
What RAD exactly is is a sliding definition. What everybody seems to agree on is that it's drag and drop. You drag something from a toolbox in your IDE and drop on something else. Dropping visual things designing (web-)forms is natural. Remember editing resource-files in the earlier days of Windows to create a form ? The Delphi and VB form designers were a great step forward. But you can also drop non visuals on your form (or component, web-service designer, etc). These "things" can be anything: an operation on a database; an interaction with the service manager; a message queue. This can be very dangerous. Someone not hindered by any knowledge of the matter will drop something which he does not understand what it actually is, let alone what it does. Like coding with a chainsaw. Cuts through the problem quickly but the damage is astonishing and impossible to repair
In the following I will demonstrate this in a small asp.net 2.0 web site. Visual Studio is a good tool to build an UI. I haven't bumped into the common troubles yet so I prefer 2005 over VS 2003. I had built a lot with asp.net 1.1. Applications which handled a lot of user input and I had to write loads of plumbing code. Many, many screens, with even more fields. Coding patterns emerged, but in the end it was still a hell of a lot of typing. The one thing which makes ASP.NET 2.0 stand out is 2 way databinding which finally relieves me of writing the plumbing code. When I study the code generated by VS and its execution flow it's not unlike the patterns I followed myself in 2003. But this code is generated a lot faster and with less errors than I can type myself.
So designing webforms with VS 2005 with a lot of drag and drop (which also works in the markup view) saves me lots of time and effort. So it's quite tempting to do the database handling the same way. In the toolbox is a sqldatasource. Dropping one on my webform is just as easy as dropping a textbox. But look what happens to the markup:
The markup should be about the way you webpage looks. But now is does contain a plain (MS) sql (server) query. When it comes to maintenance it's not a very comforting idea to store the sql strings in the markup of the aspx. Everything from the appearance to the low level database access is now in one assembly, even in one code file bringing the application down to a monolithical piece of concrete. No need to explain this is just bad, bad, bad. Here I completely agree, this kills.
It's a pity that the majority VS 2005 demos is done with this sqldatasource. In the talks by Scott Gutthrie on Asp.net 2.0 I've seen he very quickly skims over datasources, uses a sqldatasource and briefly mentions the object datasource. I don't thinks it's his favorite subject. Setting up a sqldatasource is lightning fast. That's a flashy demo and it automagically demonstrates the database connectionstring wizard, including a small "secure by default" show. A sqlExpress datasource can be quite OK for a site for some small business sites. But when the business gets big, involving DB updates, the site is toast. My advice is to stay as far away from the sqlDataSource as possible. But instead of giving up on all of VS RAD use the objectdatasource.
Setting up an objectdatasource in a demo does take a little more time. The wizard is not as flashy and has its idiosyncrasies. And it takes a lot of explaining to give the audience a clue of what is going on.
But before diving a little into them I have to mention business logic in the UI; another hobby horse in RAD. Suppose your webform wants to validate a customers name. It would be tempting to do something like this:
Now what if this fancy 73 rule changes to 84 ? You have to check all screens where it is used. Which is a (maintenance) nightmare.
An application has to be built in layers. A layer containing UI, a layer containing BL, and a layer containing DB interactions. When the layers get distributed over several processes or even machines they become tiers. For the latter to be possible every layer has to be in a separate assembly. The communication protocols between the assemblies may vary. That's a subject on itself, for the moment its sufficient to say that the things to be passed from one layer to the other should be serializable (over http) and that the communication should not be too "chatty". What I always do, in every project however small it may be, is draw the first border and add a class library to separate the non UI code from the UI.
This library is an independent assembly, it can also be used in a webservice or a winform application.
This library has a customer class to do the validation
namespace DataLibrary
{
publicclassMyCustomer
{
publicstaticstring ValidName(string myCustName)
{
if (myCustName.IndexOf("73") > 0)
{
// Your stuff here
}
return myCustName;
}
}
}
The website adds a reference to this class library (that's in the property pages of the site) and the codebehind of the web page delegates the validation to the lib.
The library is also going to contain customer classes for the objectdatasource to bind to. An objectdatasource completely abstracts the data away into any object and is not picky about the class to instantiate objetcs from, by default the wizard just lists all classes which are decorated with the DataObject attribute. The runtime will accept any class. When reading or updating data the objectdatasource will fire specific methods on the object with specific parameter lists (or one object which holds all parameters). For a good designer interaction you should decorate the implementation with the DataObjectMethodType Attribute. The names of the methods invoked to read or write data, as well as the parameter lists sent are all configurable in the properties of the objectdatasource. Which requires quite some explanation when demoing the component. The bottom line is that everything is highly configurable but takes a lot of run-time checked trial and error to get right. Having automated tests for this makes a lot of sense.
The DataSet 2.0 style is a quite handy class which can serve objects to feed to an objectdatasource, has all DataObject's attributes set, and does provide all the DataMethodType members. A dataset describes tabular data in an XSD schema and contains a tableadapter which wraps up the sql to interact with the actual database. An objectdatasource will list the datasets's tableadapters from the project and the projects referenced.
You might object that these are several layers crammed together. Later on I'll come back on that, for the moment I want to demonstrate that these dataset do have a lot of power to get your data access and logic together.
A dataset can contain more than one table. For each table there is a tableAdapter to bundle SQL queries to read and write from/to the database.
The dataset has loads and loads of wizards which will guide you through the creation of the sql statements (including scalar queries) as well as the methods published. There is an enormous code file generated. Quite interesting to walk through but not the kind of stuff you would like to write by hand.
The good thing is that a dataset can also include some business logic. Say you want something to happen every time a row is updated. In VB you can add this logic declarative to the dataset. (see this on MSDN for a deeper story)
PartialClass CustomersDataTable
ProtectedSub UpdateTimeStamp(ByVal sender AsObject, ByVal e As System.Data.DataTableNewRowEventArgs) HandlesMe.CustomersRowChanged
' Your BL here, example below
EndSub
EndClass
Now every time something uses this dataset to update data your business rule is hit..
This functionality cannot be added declarative in C#. It takes overriding the initialization code of the dataset. The eventhandler subscribing updates a comment field.
dr.Opmerkingen += string.Format("{0} :Row has changed", DateTime.Now);
}
}
The nice thing in the C# version is that you can add multiple handlers to the event. A second handler for instance to send a message to someone or something. The VB handles keyword sets only one handler. (Read here for more background on handles vs +=) To add multiple handlers in VB you have to take the C# route and override the init method as well. (Is this code available in VB ?)
So a dataset can be directly used by an objectdatasource and has the possibilities to add a little business logic to your data. But, as I already mentioned, everything below the presentation layer is still one monolithical block. When your not happy with that there are several ways to continue:
Get something to get organized. The XSD schema editor may be nice but it is not a datamodeler.
Think SOA. Your layer should be able to exchange (xml) messages. A dataset has some very useful members available. Don't expose database stuff.
Slice up the whole thing into any number of custom layers/tiers you desire. As long as you implement the methods required by the objectdatasource in one of the layers you're fine.
An 3d party external OR-mapper. No need for objectdatasource support per se, as long as the tool supports 2 way data binding. That was all we were after.
Which leads to the old dataset vs OR mappers rants. Another "yeah, but" discussion. When it comes to RAD tools I would just like to say: know what the tools do and know how to use them. Wizards are great but don't become the sorcerers apprentice.