Friday, October 15, 2004

I'm doing a little probe here.  We hear developers constantly talk about their business objects.  Pretty much every discussion revolving around application design mentions them in conjunction with UI objects and Data objects.  What are they?  What is a business object to you and what functionality does one have?

Is a business object...

  • a data container (just a fancy struct)?
  • an object that can "get" an instance of itself from the data store (via static methods)?
  • an object that can "save" itself to the data store?
  • something else?

Where do your business objects live?

  • right along side the UI objects?
  • In a separate library? separate process? separate machine?
  • in the same application but functionally distinct from the UI objects?

How much about the data store do your business objects know and understand?
How do you "get" one? via a data layer? directly?

Suppose you had a "business object" called Customer.  The Customer class is defined within a distinct library; it's physically separate from your UI and your database objects.  How would you design Customer?  How would you distinguish between a "new" Customer and one that had been retrieved from the data store (i.e. an "existing" one)?  Is there any way your data tier can distinguish the two?

For example:  Is there anything you can do to prevent the UI from creating a Customer object that, when saved, wouldn't overwrite an existing Customer from the database?  Is it important to have this level of distinction?

There's no right or wrong answer here, as the answer may be based on context or usage.  I'm just curious what anyone else has to say about these matters.  For the most part we encounter issues such as these (on some level) almost on a daily basis in designing applications.  I have my solutions, but I want to see what feedback I can get before revealing what I think would be an appropriate answer.

Friday, October 15, 2004 10:15:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  Trackback

Over the past weeks, I've had the opportunity to periodically devote my efforts to the design and maintenance of a home-grown website that I use(d) to track defects, issues, tasks, etc for my projects that I develop, among other things.  This project has been a pet project for a long time and was, in fact, one of the very first projects that I ever created using the .NET platform.

Since the time of its inception (back in 2001/2002) I have learned a lot and have wanted to take my new-found intelligence and apply it to my old project.  Unfortunately, the application had grown to a point that it wasn't feasible to simply go in with a small hand broom or a dust-vac and straighten things up a bit...it was going to take some heavy lifting.

I therefore put on the gloves and decided to rip the house down and start from a brand new foundation.  I've blogged about some of these underpinnings periodically over the past months, but I wanted to share some of the insights and inroads I've made toward the completion of the project.

First of all, the application performed extremely well with a small number of users.  Granted the application and its back-end database were on the same server.  Being that the application is an ASP.NET application, I want to be able to accommodate many (100+ simultaneously).  This application (now called DevTracker) is designed to be used by a team of developers and provide the experience that a user would expect from a Windows application.  Well, my original design was not very flexible and would be hindered with more than just a handful of users.  I had no clear separation of UI-logic and data access logic.  Except for just a few objects, the lines were very blurred.

<RANT>

In fact, I think this is one of the dangers with ASP.NET development.  Just like VB made it very easy for non-developers to be productive but at the same time create horrendous, terrible code, ASP.NET makes it very easy to create non-object-oriented websites.  Pre-.NET, the push from Microsoft was the Distributed iNternet Application Architecture (DNA).  This set of principles encouraged the separation and distinction of the logical layers (UI, Business, and Data) using technologies such as MTS and COM.  I, along with many others, subscribed to these principles.  With the advent of .NET (particularly ASP.NET) it seemed to be that the lines became more hazy.  All of the sudden people were recommending bringing DataReaders up to the UI level and binding to them, of dropping Connections and Commands right on their .aspx pages and querying the database directly.  For simple applications this might be fine, but I'd far from recommend it for anything that's gonna have to take a lot of load.

Sure, it used to be done before (even in the DNA days).  People would bind to Recordsets and perform database queries right on their pages, but while the rest of the .NET world was moving forward with nice clean OOP, ASP.NET seemed to be left behind; though I hear things will be changing with v2.0.  It's not as easy in an ASP.NET application to separate the layers - especially when it's so darned convenient to copout and do it the easy way.

I believe that developers feel that they need the raw performance available with DataReaders directly on their pages.  They don't want the perceived overhead of offloading the work to another server (say a COM+ server) when that's the kind of work that an application server does best...probably better than anyone could do on their .aspx pages.  What they don't always realize is that the physical distribution of the network can play a HUGE part in an application's performance.  If the IIS server is the same box as the SQL server, there's no IO on the NIC, so it might make more sense to bind to a DataReader.  However, if the IIS server is separate from the database server which is better? 1) performing a round trip to the database server for each DataReader.Read() call? or 2) having the application server gather all the information from the database right there and return it to the web server in one batch?  People often confuse the issues.  Few objects doesn't necessarily mean better performance.

Anyway, enough of that.

</RANT>

I wanted, in DevTracker, to design an optimal architecture that would cleanly support the physical separation of the application layers while at the same time allowing for optimal performance when located on a single machine.  To accomplish this, as a developer I have to maintain a level of discipline - I can't simply decide to do something the quick and dirty way because in the event of component relocation I don't want the application to break.

Many of the services that are presently tightly coupled within DevTracker really affect the larger whole.  These objects should be available outside the scope of the website, but still within the context of the “application“.  For example, I'm wanting to design a Windows/MSN Messenger plugin that would display a user's set of issues, giving the user direct access to them.  I also want to complete a Windows-based UI that feeds off of the same data.  If all of these objects were directly within the ASP.NET application intertwined on my pages there would be no real way to get to them cleanly.  Instead, by distributing the objects (or at least separating them), I can design interfaces (Web Services?) that expose the objects more naturally.

The best way I can envision to do this, and the obvious choice, is to make the libraries available within COM+.  By leveraging the capabilities of Enterprise Services, I am able to offload much of the horsepower to a second server.  That server therefore acts as a floodgate for all database activity, object JITA/pooling, connection pooling, security, and much more; relieving my IIS web server to do what it does best - serve pages.  So far, this strategy has proven to be beneficial and highly performant.  I'll let you all know what my performance tests yield in comparison.

Friday, October 15, 2004 9:54:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, October 08, 2004

As followers of my blog might recall, a few days ago I had my router go out due to a power outage.  Well, as it turns out, I don't believe it was my router after all.  In fact I know it wasn't.  It was my switch.  I had a switch sitting between my router/firewall and my servers and that got fried - literally.  The problem wasn't made manifest to a greater scale until today when smoke started to come out of the top of the switch.  All I can say is thank goodness I was home!  I wouldn't want to have an electrical fire start in my office.

Needless to say, I disconnected it and ran down to Circuit City and purchased a Netgear 8-port switch.  So far so good.

Friday, October 08, 2004 4:15:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, October 07, 2004

There are times in ones life where one is happy to be affiliated with great people and great organizations.  Today, I am very happy to be associated with our local Utah .NET User Group.  We have some fantastic people here that participate.  In many levels, today's user group presentations was a great success.  Rob Howard (formerly of Microsoft, now of TelligentSystems) came to our user group (via INETA) and provided a power-packed presentation on ASP.NET and some advanced, black-belt techniques for optimizing our web applications and making them as performant as possible.

As key points, Rob discussed the importance of properly using the cache.  In particular, leveraging the @ OutputCache page/control directive to cache page/control instances, but also the HttpContext.Items collection (for a per-request caching mechanism).  Rob's talk also mentioned strategies for caching data on the web server side, expiring it only when the data in the database changed.  In order to accomplish this, he proposed having a background task running in the web server that isn't tied to a request.  His solution, which I thought was pretty elegant, was to have a static System.Threading.Timer in an HttpModule (or even in Global.asax) that would periodically run and poll the database for changes.  If the underlying data had changed, the background thread would expire the cache to which the data was bound so that the next request would retrieve fresh data and cache it again for the timer to find yet again - pretty sweet solutions to common, real-world issues.

It was great having Rob there to present to us and spend time with us here in Utah!

If I have any regrets they are minor.  It turns out that INETA (the sponsor for the event) will reimburse a fixed $ amount per head at the event for food (pizza) and drinks based on the evaluations returned.  We didn't have quite as many people attend as we had anticipated so I'm going to end up in the hole some $60-$80 on refreshments...oh well, make that twice that I've ended up paying for pizza for the user group out of my own pocket.

It's all good, I'm happy to do it - I love the camaraderie and the association that I have with such fantastic people - it's well worth it.


Now, I'm looking forward to a great November meeting.  We've done some planning for what looks like it could be a great, interactive meeting.  We're hoping to have a great turnout and have lots of participation!  Stay tuned - I don't want to reveal too much yet...but it'll be loads of fun!

Thursday, October 07, 2004 8:57:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, October 06, 2004

Today is a very exciting day for me (as well as those in our local Utah .NET User Group).  It is with great pleasure that we welcome Rob Howard (formerly of Microsoft on the ASP.NET/.NET Framework teams and now founder of Telligent Systems).  Rob will be providing a presentation entitled “10 'Killer' Performance Tips and Tricks for ASP.NET”.  I'll provide a report of the event later tonight after the meeting...this is going to be awesome!

If you'd like to attend, please 1) RSVP and let me know and 2) show up.  It's free and anyone and everyone is welcome.  Our local .NET User Group meets at Northface University which is kind enough to offer us space at their facilities each month.

Here are the details about tonight's meeting:

Date: October 06, 2004
Time: 6:00 PM
Northface University
10701 South River Front Pkwy, 2nd Floor
South Jordan, Utah 84095

Wednesday, October 06, 2004 8:04:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

I have found it's a lot easier to blog about solutions to problems because of the turmoil and heartache involved as well as the victorious feeling of relief when the issue has been resolved.  In an attempt to be more proactive to coding solutions, rather than reactive, I will work on providing more valuable blog entries not so much about problems overcome, but techniques and strategies that I employ in my day-to-day programming because I feel that some of that experience would be valuable to someone out there in the wild wild web.

Today's blog entry, however, is not one of those blog entries.  I'm going to talk about a problem solved.  I was taught a very valuable lesson today - again.  Here's the deal:

I am working on an ASP.NET application that dynamically loads various controls depending on the user's context with the application.  Some of the controls I wrote as Server Controls while others are User Controls.  Within a particular user control (.ascx) I had placed some hidden controls (HtmlInputHidden) that provide UI feedback to the server so I can take appropriate action.

When the form was posting back to the server I couldn't see the values within the controls.  That is, the .Value property would ALWAYS return the default value (at least it would return the value assigned in the <input /> tag).  No matter what I did.  Interestingly, however, I could SET the value and that would get posted back to the server.  The only way I could ascertain this was to inspect the form's posted values - indeed the correct value was always there, but it was never associated with the control variable.

As it turns out, and it is with much embarrassment that I even admit this, I was loading the control too late in the hosting page's lifecycle.  I had the call to LoadControl(”...ascx”) in the OnLoad() method.  Instead, it belongs in the OnInit() method andthat's because the ViewState is loaded back in the controls after the OnInit and before OnLoad.  I don't know how I overlooked that, but I did.  I don't think I'm going to forget that lesson ever again.

In order to properly associate control values, property values, events, etc with dynamically loaded user controls, you must always instantiate the control in the OnInit() method (or the Page_Init() event) and add it to the parent control's .Controls collection therein:

protected override void OnInit(EventArgs e) {
   MyControl ctl = (MyControl)LoadControl(”MyControl.ascx”);
   Page.Controls.Add(ctl);
}

Wednesday, October 06, 2004 7:56:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, October 05, 2004

Sorry for the non-technical post, but I must agree with Sam Gentile.  I saw Sky Captain and the World of Tomorrow on opening weekend and thought it was a blast.  I couldn't precisely nail down what I liked about it, but I think Sam hits the nail on the head: it's a mixture of (semi-)modern sci-fi with a Flash Gordonesque, 30's style.  I absolutely love the pastel, sepia color used throughout the flick.  I am definitely one for nostalgia, and watching this movie I felt once again like a kid.  Watching the characters travel over the globe with the super-imposed lat/long lines and compass beneath the surface of the water was fantastic; that and the abnormally enlarged radio tower summoning Sky Captain...Watching it reminded me too much of the old George Reeves Superman...though the movie wasn't that corny.

I normally wouldn't be posting something like this, but it turns out that during my weekly programming session with a group of friends last night we were talking about just this movie.  Then reading Sam's blog I couldn't help it.  Go see the movie if you haven't yet!

Tuesday, October 05, 2004 7:45:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, October 04, 2004

By no stretch am I an Xml guru.  I know enough to get by and can be pretty dangerous with it.  I stumbled upon this tool by Jeff Key that I find extremely useful.  I deal with a lot of XML as single, long strings.  I want to view the Xml in a hierarchical node-tree view and while IE gives me that (to a limited degree), this tool gives me much more power with a very small learning curve.

  • You can view your Xml loaded from a file
  • You can view your Xml loaded from the clipboard.  Nice!  While this functionality might be expected, I'm glad the Jeff took the time to support such functionality.
  • Organizes your Xml indented (regardless of whether it came in in that format or not)

Now if you're wanting more power and capabilities, but at a steeper learning curve, there is also the XMLSpy 2004 Home Edition (which is free) or the Professional or Enterprise versions.

Unless I absolutely need the added functionality, though, I'll be sticking with Jeff's tool...thanks Jeff!

Request:  Support XPath queries for searching; it would help facilitate using a single tool for my basic XML needs, rather than having to jump between tools to perform those queries.

As you can see, sometimes the simplest of things gets me excited because I've been wanting this capability for a long time, but haven't taken the time to write it myself.

Monday, October 04, 2004 8:47:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Saturday, October 02, 2004

I consider myself a pretty good ASP.NET developer.  However, it doesn't matter how many times I try to do it, I can't seem to remember how each of the path functions on the Request and Page/Control objects work.  Invariably I find myself opening MSDN or tediously debugging and stepping through my code, making heavy use of the Command Window or the Quick Watch dialog.

Thanks to Rick Strahl for consolidating these functions in a manner that's easy to read and understand. :-)

Saturday, October 02, 2004 3:54:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, September 30, 2004
I was catching up on some blog reading and ran across this link in Eric Gunnerson's C# Compendium.  Great game that tests your ability to not follow your instincts.  Fantastic game!
Thursday, September 30, 2004 4:07:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback

If you're a website developer you may have run across several of the nuances presented with the more secure IE 6 for Windows XP SP2 - namely the Information Bar (the nice yellow bar that appears at the top of the webpage when the page attempts to perform some restricted action such as display a popup the user didn't request, or the unsolicited installation of an ActiveX control).

Here are a couple of great links to follow to learn more about those restrictions and how to make your website play friendly in their new environment:

Compatibility in Internet Explorer 6 for Windows XP Service Pack 2

Fine-Tune Your Web Site for Windows XP Service Pack 2

Thursday, September 30, 2004 3:50:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, September 29, 2004

Last night I was greeted to a very unpleasant, and unsettling occurrence that had me baffled until today when I got some help from a great friend - one of my old co-worker buddies from Microsoft.  My network was up and everything was fine (internally), but I couldn't get any DNS resolution outside my network (i.e. out on the internet).

As it turns out I could ping my router from every computer on the network except my domain controller (which is also my DNS server).  Because my DNS server couldn't resolve anything, I was pretty dead in the water.  I tried several things to fix the issue, but nothing worked.  It was suggested by my buddy to change the server's IP address and see if I couldn't still ping the router.  Lo and behold that was the problem!

Somehow, somewhere, the router was denying traffic from my old IP address.  Without any concrete evidence, I attribute this problem to a pretty bad power outage that we had a few days ago.  Even after powering down the router for several minutes and starting it back up I could not get connectivity on the old IP so I had to resort to a new IP.  Crazy, but at least it works!

Ya know? I've been in the contemplating getting a new, good, reliable router anyway...maybe this is the incentive that I need.  This one that I have (a NetGear) has been fantastic over the past several years, but I've been wanting to get something newer and better; budget is not the primary concern - but practicality is.

Any suggestions?

Wednesday, September 29, 2004 3:24:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback