The value of MCSD for .Net – level 000

I’ve come across many discussions about the merit of Microsoft certifications with some claiming that it makes cream rise to the top and others claiming that any moron with the time to read a book could pass the tests.  I don’t agree with either viewpoint, but I did earn an MCSD.Net credential.  My conclusion is “it can’t hurt”. 


The following article is worth reading, though, on the effect of certifications and salary.  You have to adjust numbers depending on your area’s cost of living, but it’s a good read: http://www.mcpmag.com/salarysurveys/


Also, dig a little deeper to get a feel for the relationship between years of experience and the salary because that also has a big effect.


And last but not least, a big plug for the Pre-Tech Ed Party with Palermo.

Pre-Tech Ed party is ON. Here are the details. Please pass the word – level 000

I am hosting a pre-Tech Ed party on Saturday, June 4th.  That is the day before the pre-con.  The Peabody hotel is just across the street from the conference center, and that is where we will meet.  Then, later on that evening, we’ll have a large geek dinner at the restaurant in the Peabody lobby (I’m assuming everyone won’t have cars to enable driving a few miles).


Party with Palermo is the official title (from Darrell Norton).


Here is the itinerary:


4PM – Arrival begins.  meet and greet.  Discuss conference sessions, etc.  Networking.
6PM – Geek Dinner.  I have reserved for 20, so I will need actual names for people to expect.
7:30PM – Drinks and conversation at the hotel bar.
Depart whenever.


The following is important.  Use the contact form on my blog to let me know you are coming.  I initially reserved 20, but when I get more (there may be 100 or more), I’ll need to adjust the reservation.  It’s very important that everyone planning on attending sends me an email through my blog so that I have you on the list for dinner.  I’ll also be able to send out targeted last-minute updates.


Please spread the word.  I’d appreciate linkbacks if you have a blog as well as email referrals to colleagues who will be at Tech Ed. 


From this point on, I’ll prefer to send updates directly via email, so be sure you send me your contact information so I can keep you in the loop.

Win Server 2003 as a development environment – level 200

When I installed Server 2003 on my development laptop, I had to jerry-rig some XP drivers in order to use all my devices, but I’ve been using this OS for all my development for almost a year now, and it has worked out very well.  It has been just as stable at XP and very nimble, too.  I use a Dell Lattitude D600 with P-M1.8Ghz and 1GB RAM.  The only complaint I have is MS’s lack of user support for Server 2003.  For instance, I can’t use Windows Movie Maker on this OS, but those are little issues.


As a development workstation, I’ve been very satisfied.  With XP, you are limited to one top-level website.  With server 2003, it can be many.  This is great if you do ASP.NET.  Also, it supports multiple remote desktop sessions.  I’ve had a very good experience with it.

Displaying aggregates: DataSet vs. Domain object performance – level 300

The first thing that happens when someone posts performance numbers is to question the method of benchmarking.  My test was an ad hoc benchmark on my own workstation to get a feel for the performance difference, if any, between bringing aggregate data from a database using a DataSet and using a collection of domain objects.  Some have said that binding to collections of domain objects is a “heavy-handed” approach when all that is required is a read-only display of the data.  My opinion was that the DataSet, with its deep and complex object model, is slower(and more memory intensive) than using custom domain objects for this task. 


I created a read-only view of the customers table in the Northwind database.  I created a Customer object and CustomFactory for presenting an aggregate of Customer objects (one for each row).  I also created an object to return a DataSet with this same information.  I created two ASP.NET pages identical with a single DataGrid (with Viewstate off).  One bound the array of Customer objects, and one bound the DataSet.  Then I used Application Center Test to profile each page.  I made 2 runs each for 1 minute each. 


The page that used an array of custom domain objects to present a read-only display was 3.7% FASTER than the same functionality using a DataSet.  I’m not interested at all in the performance difference, but all I care to prove is that using custom domain objects is NOT a heavy-handed approach to displaying read-only aggregates of data.  Besides that, it makes for a better application design with cohesive logic.


I prefer to use custom objects for just about everything.  Yes, a DataSet does it all, but at the expense of everything else.  If I need a few rows from a table, let’s take a look at all the objects I have to create just to get that:



  • DataSet object

  • DataTable object

  • DataRowCollection

  • DataColumnCollection

  • DataRow object (for every row – this normally has a slightly bigger footprint than one of my custom objects)

  • object for every field in every DataRow

  • DataColumn object to define information about every column

  • DataRelationCollection

  • DataViewManager

  • PropertyCollection

  • ConstraintCollection

It seems to me that using a DataSet is a heavy-handed approach to moving data, but given that the performance difference is so small, my main reason for choosing custom domain objects is design.


My main motivation for posting about this was to disprove the misconception that using custom domain objects for read-only aggregate views is an overkill.  In fact, there is no proof of this performance difference by using the OO design.

The _real_ reason to shy away from DataSets – level 300

If the title of this post gets you worked up, you probably shouldn’t read on.  I’m about to bash your beloved DataSet.


Scott Mitchell has written more on why to avoid DataSets.  While he touches on performance and the mechanics of what it does, I believe the issue is bigger.


Everyone can argue and compare performance numbers in different scenarios to justify one versus the other.  The real argument, I think, is design.  No matter what you do, your application must perform well enough to meet the customer’s expectations.  The customer doesn’t care about the internal workings.  So what other motivations do we have? 


What about OO design.  What about an object owning state and all behavior that goes with that state?  A DataSet cannot substitute for a domain object.  It has no custom behavior.  It holds records.  As long as DataSets are pulled and bound to the UI, the application will be of Procedural nature.  I’m coming from a stand-point where I care about OO and the maintainability and testability gains associated with it, so DataSets don’t hold much weight with me. 


Now we are talking about another issue altogether.  Are you dragging and dropping a RAD application that will have a short lifespan (because RAD and Maintainability are antonyms)?  If so, you can stop reading now because this discussion has no bearing on your application.


If your intention is to develop a well-factored object-oriented system with loose coupling between layers and high class cohesion, then please read on.  You will want to identify entities in your system and develop your domain object first, NOT your database schema.  If you start with the database schema first, you are starting with a handicap that is often hard to overcome.  Start with your domain objects first and define the entities that your system will manage.  Each of these entities will own some state and will encapsulate this state with custom behavior through their methods.  One example is that if you have a Product object and you try to set the Price field to a negative number, the object shouldn’t allow it.  The object is responsible for protecting its state.  If you have a dumb data container (DataSet) floating around, then you have to jerry-rig some validating logic before you “Update” the changes back to the database. 


If you have a well-factored OO design, you will always be able to extend the system with new functionality as well as change existing functionality with minimal impact.  This can exist because each small responsibility is housed in its own object and not shared among the logical layers.


I really don’t care about convincing DataSet users to enroll in Rehab (unless they are on my team – which they aren’t).  Good design isn’t easy, but the benefits are worth the effort.  DataSets are easy.  Speaking of easy. . . here are some other things that are easy:



  • Maxing out your credit card
  • Not paying your bills
  • Sleeping in
  • Settling for mediocrity
  • Posting an nonobjective comment to this blog post

For the record, I have used a DataSet (and a strongly-typed one), but not any more.


ADDITION:


I must say that the use of a DataSet object in an application doesn’t condemn it’s design to the lake of fire, but relying on that as your _business object_ does.  If there is a situation where the overhead is acceptable, use it _inside_ your business object or somewhere else, but protect your entities.  Don’t pass around naked entities in a DataSet exposed to the world.  Use encapsulation.  Protect your data.


Perhaps my main beef is with the use of DataSets as promoted by the VS.Net drag and drop tools.

Senior .Net/C# Devs needed at Dell – level 000

I’m looking for experienced .Net/C# developers to work on Dell’s call-center application for the sales force.  The more business experience the better.  These positions are for very senior developers with a lot of high-level business knowledge.  I can connect you directly with the hiring manager if you email me at jeffrey_palermo (AT) dell (DOT) com with a resume.  Or use my contact page here on my blog.


This is full time salary + benefits in Round Rock, TX.  Believe it or not, there is a shortage around here of good senior developers.  If not, we’re having a hard time finding them.  If you’re good, then we want you now (in the next month).  These positions are not entry level or intermediate.  Looking for level 300-400 developers only.


If you’d like the HR job description (which I never like),  go to Dell.com and search for job “050004MM”.  This is a sample Req.  The application is the Windows Smart Client desktop app that the sales people use to manager customers, issue quotes and take orders for call in orders and our large customers.  Basically all orders that don’t come in over the web.  It’s currently rolled out to about 8000 salesmen.  The software uses the IDD (Integrated Dell Desktop) application framework that MS did a case study about: http://www.microsoft.com/resources/casestudies/casestudy.asp?CaseStudyID=16276

Use parts of the ASP.NET runtime as providers for services the business layer might need – level 300

We all know to use the Strategy pattern to let the domain assembly know where to get and persist data.  Our domain assembly has no references to any other custom assembly, but all assemblies reference the domain.  We use the Dependency-Inversion Principle to accomplish this for data, but what about other services?


For instance, what if different domain object would benefit from knowledge of some state that is not AppDomain-specific, and not user-specific, but request-specific?  The ASP.NET runtime offers a great statebag for request-specific information.  HttpContext.Items.  This collection of key/value pairs can be used in an ASP.NET context.  Of course, we don’t want our model to depend on the ASP.NET runtime.  In fact, our model doesn’t care.  It will not reference any of our assemblies and especially not System.Web.  How then can we make a domain object interact with this Items collection? 


Simple.  Use a Strategy that allows injecting an adapter class into the domain object.  The adapter class can live at the ASP.NET runtime layer and server as a proxy for interaction.  First, in the domain layer, we must define the appropriate interface required:


    5 public interface IUserContextCacheable
    6 {
    7     void SaveUserState(object key, object state);
    8     object GetUserState(object key);
    9 }

Any class implementing this can serve as our state or cache provider.  For this example, I have two domain object that will consume this.  There are many ways to inject a dependency, so use your imagination.  I’m presenting the simplest way.


    5 public class Customer
    6 {
    7     public void DoSomething() {
    8         // Do something and save the state in cache.
    9         UserContextCache.Instance.SaveUserState(“MyState”, “I went to the store at ” + DateTime.Now.ToString(“hh:mm:ss”) + “.”);
   10     }
   11 }


    5 public class CustomerTracker
    6 {
    7     public string GetWhatWasDone() {
    8         // Check to see if something was done.
    9         object o = UserContextCache.Instance.GetUserState(“MyState”);
   10         if(o != null) return o.ToString();
   11         else return null;
   12     }
   13 }

Customer will save a piece of state, and later, CustomerTracker will benefit from it being there.  In our domain layer, we also have a singleton to hold an instance of IUserContextCacheable.  This singleton will be set by the ASP.NET layer at startup.


    3 public class UserContextCache
    4 {
    5     private static IUserContextCacheable _singleton;
    6 
    7     public static IUserContextCacheable Instance {
    8         get { return _singleton; }
    9         set { _singleton = value; }
   10     }
   11 
   12     private UserContextCache()
   13     {}
   14 }

Now, in our ASP.NET layer, we have this class:


    6 public class RequestCache : Model.IUserContextCacheable
    7 {
    8     public void SaveUserState(object key, object state) {
    9         HttpContext.Current.Items.Add(key, state);
   10     }
   11 
   12     public object GetUserState(object key) {
   13         return HttpContext.Current.Items[key];
   14     }
   15 }

It implements the required interface, and our controller will use an instance of this when constructing the domain objects.  An instance of this is set to the singleton in the domain layer using our Global.asax (you could use a number of other methods as well).


   24 protected void Application_Start(Object sender, EventArgs e)
   25 {
   26     Model.UserContextCache.Instance = new RequestCache();
   27 }

Here’s a snippet of code that, for demonstration purposes can be placed in any page or user control (or any control for that matter):


   22 this.TraceEnabled = true;
   23 
   24 Trace.Warn(“To prove that no previous state exists, I’ll try to get ‘MyState'”);
   25 Trace.Warn(“MyState = ” + this.Context.Items[“MyState”]);
   26 
   27 Trace.Warn(“Invoking business object, Customer, to do something.”);
   28 Customer f = new Customer();
   29 f.DoSomething();
   30 
   31 Trace.Warn(“Invoking CustomerTracker, another business object that benefits from this knowledge.”);
   32 CustomerTracker b = new CustomerTracker();
   33 string state = b.GetWhatWasDone();
   34 
   35 Trace.Warn(“Bar used the following state: “” + state + “””);

When you run this page, you’ll see Trace enabled and the following outputted to the screen:



































































Trace Information

Category Message From First(s) From Last(s)
To prove that no previous state exists, I’ll try to get ‘MyState’
MyState = 0.000013 0.000013
Invoking business object, Customer, to do something. 0.000032 0.000019
Invoking CustomerTracker, another business object that benefits from this knowledge. 0.000734 0.000701
Bar used the following state: “I went to the store at 07:17:18.” 0.001141 0.000407
aspx.page Begin PreRender 0.001162 0.000021
aspx.page End PreRender 0.001181 0.000019
aspx.page Begin SaveViewState 0.001252 0.000072
aspx.page End SaveViewState 0.001374 0.000122
aspx.page Begin Render 0.001390 0.000016
aspx.page End Render 0.073195 0.071805



This proves that our ASP.NET runtime adapter class works as intended and allows our domain layer to store things in the ASP.NET runtime.  This prevents us from having to implement our own caching mechanism that has the correct scope.


Using the Strategy pattern, we can hook up pretty much any outside service with an adapter class (adapter is my word). 


I’ve zipped up this sample solution, and it is available on my downloads page.