CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeffrey Palermo (.com)

Blog moved to www.jeffreypalermo.com

May 2005 - Posts

  • 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.

  • The effectiveness of TDD, a whitepaper - level 200

    Some researchers from North Carolina State University have done a small study on Test-Driven Development and published their results: http://collaboration.csc.ncsu.edu/laurie/Papers/TDDpaperv8.pdf
  • 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.

  • ASP.NET 2.0 Beta website and quickstarts - level 200

    Check out http://beta.asp.net/, for ASP.NET 2.0.  It has announcements and quickstarts and other resources to help one get started with ASP.NET 2.0. 
  • 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.

  • Calling all Texas code wranglers - Texas .Net Olympics - level 000

    The .Net user groups in Texas are working together to put on a .Net Olympics.  If you hang your hat in Texas, then we want you!  It's very simple.  Submit an application you have developed that hasn't been released yet.  There will be prizes for winners in every category.

    Check out http://dotnetolympics.net/ for all the details.  Create a log-in to become a contenstant.

  • 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.

  • All Tech Ed sessions and the schedules are now posted - level 000

    If you are going to Tech Ed, check out http://www.msteched.com/content/sessions.aspx.  All the sessions are scheduled now including BoF sessions.  Click on the “Speaker” drop down and look for “Jeffrey Palermo”.  It's not every day that Microsoft.com publishes my name right next to Jeffrey Richter's.  What an honor!  Actually it's just a coincidence related to the alphabet.

    Anyway, the closer the date gets, the more pumped I am.  I look forward to some great learning and facilitating by Agile Methodologies with .Net BoF session.

  • Taking suggestions for the Pre-Tech Ed party on Saturday before the conference - level 000

    Ok, I've gotten a great response from others arriving at Tech Ed early, so this party should be a lot of fun.  I have some ideas myself, but I'd like to take suggestions (from those who plan on attending) on the following items:

    1. What time it should start
    2. Where I should book it
    3. What activities to include
    4. What restaurant to include (because we must certainly kick off the week with a geek dinner!)

    Here's one idea.  If everyone can get a class M license before June, we can all rent Harley's from EagleRider's in Orlando. :)  I had a Fat Boy for a day back in 2001, and Orlando is a pretty nice town to ride through.

    That was a joke, but now that I think about it, I'm actually arriving the evening of the 3rd (Friday), so if any other bikers are as well, then maybe we can get our rentals and have a Pre-Pre-Tech Ed Party Biker Run.

  • Visual Studio solution file isn't Xml - level 100

    I had to manually edit a Visual Studio 2003 solution file today.  I changed the source control location for the contained projects, and I found it easier just to edit the solution file than to use the GUI to fix it.  One thing I found particularly odd is that the solution file isn't Xml.  Project files are Xml, but not solution files. 

    Whidbey doesn't make solution files Xml either.  I wonder why a custom format would be chosen over Xml.  With backward-compatibility being such a high priority, I now wonder if the solution file with _ever_ be converted to Xml.

  • Jeremy Miller on TDD - level 300

    If you are interested in good design, you must check out Jeremy Miller's blog.  He has some great TDD info in plain, distilled language.
  • Give your DLL a dedicated config file - level 300

    Warning:  the following technique uses a child AppDomain and remoting.  If you don't understand these concepts, then you don't have a need for this technique.  If all your code runs in a single AppDomain, then all config keys will have to go in the <process>.config file.  In that scenario, if you need additional configuration, you'll have to use another persistence medium such as a database or custom Xml file.

    With that being said, sometimes it is appropriate to run some code in a dedicated AppDomain and shield it from the rest of the process.  In this case, the entry point will be up to you, and the default config file will also be up to you.  You can run a particular class library inside an AppDomain and give it a dedicated config file.  NUnit uses this technique to allow test assemblies to be configurable by naming your config file FooTestAssembly.dll.config.  In a particular application, I have several “plugin” class libraries, and I need each to have dedicated configuration.  I'd like to use the built-in appSettings because this is the “simplest thing that would work”.  Here is how I go about doing this.  You can also download the complete sample solution at my downloads page.

    I'm going to create a simple console app to be the entry point for my application:

        5 namespace ConfigTestHarness
        6 {
        7     class App {
        8         static void Main(string[] args) {
        9             AppDomainSetup ads = new AppDomainSetup();
       10             // Set the new AppDomain's config file any way you see fit.
       11             ads.ConfigurationFile = "ConfigTestClassLibrary.dll.config";
       12             AppDomain ad = AppDomain.CreateDomain("Whatever name I choose to give", null, ads);
       13             // Create a MarshalByRefObject class in the new appdomain and call it
       14             // First argument is the assembly without the .dll.
       15             // Second argument is the fully-qualified type name.
       16             Foo app = (Foo)ad.CreateInstanceAndUnwrap("ConfigTestClassLibrary", "ConfigTestClassLibrary.Foo");
       17 
       18             // Ensure this class can get keys from the primary config file.
       19             Console.WriteLine("Main program config value: " + ConfigurationSettings.AppSettings["consoleKey"]);
       20             // Ensure that class running in separate AppDomain can access dedicated keys.
       21             Console.WriteLine("Class library value: " + app.GetConfig());
       22 
       23             Console.Write("Press enter to exit.");
       24             Console.ReadLine();
       25         }
       26     }
       27 }

    Notice how I use one configuration item in the default app.config file for this console app?  Now, here is the code for my simple class library:

        4 namespace ConfigTestClassLibrary
        5 {
        6     public class Foo : MarshalByRefObject
        7     {
        8         public string GetConfig() {
        9             return ConfigurationSettings.AppSettings["classLibraryKey"];
       10         }
       11     }
       12 }

    There is no magic here.  It accesses config settings just like any other code would.  The trick is the context in which this code runs.  This code is running in a new AppDomain I created.  I set the configuration file explicitly to "ConfigTestClassLibrary.dll.config".  You can use any method you'd like to “discover” what name you would like to use, but this is simple.  Now, when my AppDomain is created, that config file is automatically used.  They are isolated.  My class library code can't access the default config file of the exe, and the exe can't access "ConfigTestClassLibrary.dll.config".  AppDomain completely partitions context.  Memory isn't shared, static state isn't shared, and configuration isn't shared.

    Using this method, every application plugin I write can run in its own memory space and own its own configuration.

More Posts

Our Sponsors

Free Tech Publications

This Blog

Syndication

News

Headspring Systems

View Jeffrey Palermo's profile on LinkedIn

See my new blog at .jeffreypalermo.com