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

Karl Seguin

.NET From Ottawa, Ontario - http://twitter.com/karlseguin/

December 2006 - Posts

  • MS Consulting : One Consulting Company To Rule Them All

    warning: this post is based on highly ignorant beliefs and huge generalizations. I know that there already is a Microsoft Consulting branch, but the one I'm speaking of is completely made up –since I know nothing about the existing one.

    A company who seeks out a software consulting firm to help build [part of] a custom application likely has a simple incentive: to receive a functioning application at a fair cost. There are many reasons why a company might turn to a consulting firm - the main ones probably being lack of time and/or resources.

    The problem with software consulting firms is that their incentives likely don't line up with their client's. A consulting firm's objective is to make money. The two goals aren't necessarily irreconcilable. A happy customer can certainly contribute to a consulting company's bottom line, through repeat business and word of month.

    Of course, there are a lot of ways for a consulting company to achieve its goals that aren't in the client's best interest. A consulting company might produce low-quality code which may or may not be functional. They might charge to fix errors in their own code. They might charge unfair rates for more junior developers. They might share resources with other projects (which may or not have been agreed to). In fact, there are more ways that a consulting company can squeeze a dime out of a clients hand than I care to think about.

    In my limited experience, large consulting companies are more likely to serve their own interest because they are (a) not as concerned with word of mouth (i.e. they have a world-wide recognized name) and (b) are interested in volume business. As a matter of fact, I know of one very large consulting company which rates its employees based on how well they represent the consulting company, but doesn't have 1 field in their review which recognizes their contribution to the clients goals.

    This is far from a unique or new problem. For example, most of us are at our mechanics mercy when it comes to any car issues we might have. At a high level, the solution to any of these types of problems is to align both parties' goals as best as possible.

    Enter Microsoft.

    Assuming we are strictly talking about Microsoft technologies, Microsoft is best positioned to solve the problem. You see, Microsoft's incentive, like consulting companies, is to make money. What makes them different is that they make money by selling software – not services. Their service offering, in this case Microsoft Consulting, could be set up as a loss leader. They could offer consulting services at-cost, run the entire division as not-for-profit and completely transparent to clients, while increasing their product sales and client goodwill towards them.

    In a recent project I was involved in, the client completely abandoned the Microsoft platform due to horrid execution by the contractor. I have no doubt that, by no fault of its own, Microsoft lost millions in software sales.

    Running in non-profit mode, Microsoft Consulting would be able to offer stupidly better rates than any other major player.  For $500/day, Microsoft Consulting could provide a consultant that others would easily charge $1500 for. The overall drive for quality would also be somewhat better aligned with the client's expectations.

    Of course, there are flaws with my approach. First, it assumes that Microsoft Consulting is able to deliver quality products, hire quality developers and properly manage them. Secondly, since they make their money on software sale, they aren't likely to suggest MySQL or SQL Server Express – even if they happen to be the better choice in a particular case.  Consultants would likely be pressured to push Microsoft technologies that really aren't necessary (i.e, build something for InfoPath and require the company to buy 3000 copies of the program).

    Another solution would be for Microsoft to stop giving out partner points and certification like candy – actually make companies earn and prove their capabilities. My understanding of building an Xbox game is that Microsoft has to approve the game before it can ship. "Gold certified partners" or whatever should have to go through the same scrutiny and go through it often. I'd be the first to apply for such a job. Not doing so just hurts them in the long run.

    Something needs to be done to solve the issue. There are small companies out there which specialize in architecture review – which I think anyone spending a fair chunk of change on custom software should consider doing.  Independent reviews are key – else you'll get screwed big time. And Microsoft's responsibility doesn't stop once VS.NET is shipped – they owe it to their own brand to ensure their tools are responsibly and properly used.

     

  • Database Impedance Mismatch

    I've always been pretty vocal about my anti-dataset views. I also don't think databases should be treated as dumb repositories - they should be leveraged. This in-between attitude, which I think is actually fairly common, means I end up writing a lot more code than either dataset users or OO purists.

    Extra code isn't really a problem -  I can always codegen it or write it in my sleep. I think the biggest problem with being an in-the-middle-kinda-coder is that the impedance mismatch is very present for even the most trivial task. Take for example the very typical problem I'm currently having: returning values from a stored procedure and mapping them to an enum.

    I have a Friends list, and calling the sproc AddFriend might return a InvalidUserName, AlreadyAFriend or BeingIgnored status. Sure, I could raise exceptions (assuming the DB supports something like that), but I don't consider those to be exception worthy. I have an enum defined in my C# code which I'd like to map these values to, but how?

    I can pass each status into my sproc as a variable, something like:

    command.Parameters.Add("@InvalidFriendNameStatus", SqlDbType.Int).Value = AddFriendStatus.InvalidFriendName;

    I could hard-code the values in my sproc, or use a table to replicate my enum. I could even make the entire thing DB driven, and on app startup, load fake enum values from status tables into specialized classes...I never know what's the right way to do it. I'd actually consider using SQL Server's CLR if it was an option..

    I don't think impedance mismatch is actually less of a problem if you are more data-centric or more object centric. In my experience you'll run into less small problems, but more serious ones.
     

    help...

     

  • Resharper holiday pricing

    David beat me to the punch about the release of Resharper 2.5 which is a free upgrade for existing customers. I've just installed it and the performance improvements are noticeable, yay! The minor issues I was having with Vista also seem gone. Finally, there's some very preliminary support for VB.NET which I unfortunately won't get to try out any time soon.

    What David didn't know at the time is that you can buy Resharper Pro for $99 until January 11th (according to the Resharper blog anyways). There's also a special discount when you buy Resharper Pro and dotTrace together. Resharper is by far my most recommended .NET tool (free or commercial) if you use C# a lot. There's a free fully-functional 30 day trial.

    An alternative to Resharper which is also very good (and has great VB.NET support) is CodeRush.  

     

  • How to hire a programmer - Part 3 - Solutions

    Part 2 featured a piece of smell code and asked for ways to improve it. The goal isn't necessarily for someone to catch all the issues but at least get the big ones and be able to talk about the code. A couple small things did get past everyone :)

     The stuff most people caught:

    • Some objects aren't being properly disposed of. If it implements IDisposable, dispose of it! I really should be using the "using" or try/finally block in my data access functions.
    • There's exception swallowing when parsing an int (I'll talk more about this a little further down since some people had different views on how to do it right)
    • Connection string is hard-coded
    • SELECT * instead of specific columns
    • I'm using string concatenation for my SQL command...should use Parameters

    On that last point, some people suggested avoiding inline Stored Procedures. I tend to agree, but either way, use parameterized queries! Sometimes I think people don't realize they work just fine for inline-commands as well. Although my use of string.Format with an integer data-type is probably safe, it's just asking for trouble (and as someone else said, telling all the other developers on the team that that's the way to do things).

    Most people suggested a better architecture to the code, namely separating my data layer, using custom objects, a base page and applying a MVC pattern. At the least, I'd expect an interviewee to suggest moving out the data layer. Some of the comments were really open minded though and suggested improving the architecture only if this was part of a bigger project, otherwise the sloppy codebehind was probably ok. That shows a lot of maturity and understanding :)

    Some of the items that got by most people:

    • When checking the cache for a value, always assign it a hard reference, else it might be gone by the time you actually try to get it. Duncan managed to get it. So instead of the code I have, you really have to do:

    DataTable dt = (DataTable)Cache["AllCategories"]
    if (dt == null)
    {
       ...get your dt
    }
    return dt;

    • Mark was the only one who caught that base.OnLoad(e) should be called regardless of the Postback status!

    About that...it seems like at least a couple of you didn't particularly like me overriding the OnLoad and OnInit methods. I'll do a bit more research on it this week, but it's actually how I do it (I've changed my page template). Personally, I find the AutoEventWireUp to be a completely unnecessary abstraction of what's actually happening - overriding the base classes function.

    As for the minor issues that seem to have gotten past everyone's radar:

    • Although there's no harm, it doesn't make any sense to worry about Request.QueryString["categoryId'] during a postback. The parameter will only be used during the initial hit to the page. Everything in the onLoad function, except base.OnLoad  should be in a If Page.IsPostback
    • My Search_Click function blindly accepts user-input and converts it to an int. The worse that'll happen is that it'll crash for that user (which I can normally live with), but it's probably something we can handle.

    So the last issue is how to properly parse/deal with the categoryId input from the querystring.

    Rondel suggested:
    int defaultCategory = Int32.Parse(Request.QueryString["CategoryId"] ?? "-1");

    but that'll crash out if CategoryId isn't null but isn't an integer either.

    The consensus seemed to be to use the new Int32.TryParse (maybe short-circuiting a null check in before), which I agree with. DaRage points out that TryParse uses exceptions to do it's thing (damnit, I reformatted and don't have reflector on here yet). I think that's a good point that's worth keeping in mind,but I still think it's the right way to do it. Yes, you should only rely on exception handling for exceptional situations (crappy user-input on the querystring isn't exceptional in my mind), but that's something inside a .NET library...it's the .NET team's fault for relying on exceptions inside a function that was specifically meant to be used in this kinda case

  • How to hire a programmer - Part 2 - Improve this code

    In Part 1, Christopher pointed out that since we all spend a lot of time maintaining and fixing code, a good interview test is to have people fix up some smelly code.

    Since I couldn't possibly agree more, I wrote a smelly codebehind page as an example of the type of test you might ask a potential candidate. I wouldn't expect someone to catch ALL the issues, or even agree with me that something actually IS an issue...but there are certainly some key items which should be fixed, and others which should atleast we discussed.

    You could ask them to rewrite the code. You could ask them to talk about what they'd do, or number issues and provide a corresponding numbered list of suggestion/comments

    I'll post my own "solution" to this "problem" next week :)

    The test...

    We have built a very simple page where clients can search our list of products by category. There are two ways to change category, either via a dropdown on the page, or by passing a categoryId in the querystring (so other pages can link directly to a category). There isn't anything functionally wrong with the site and we love the look (i.e, don't waste time changing how it looks), but the code is a little messy and we'd like to clean it up. There are no tricks or secrets. Here's what the page looks like:

    Screenshot

    Here's the codebehind file:

    public partial class search : Page
    {
       protected override void OnLoad(EventArgs e)
       {
          int defaultCategory;
          try
          {
             defaultCategory = Int32.Parse(Request.QueryString["CategoryId"]);
          }
          catch (Exception ex)
          {
             defaultCategory = -1;
          }
    
          Results.DataSource = GetResults(defaultCategory);
          Results.DataBind();
    
          if (!Page.IsPostBack)
          {
             CategoryList.DataSource = GetCategories();
             CategoryList.DataTextField = "Name";
             CategoryList.DataValueField = "Id";
             CategoryList.DataBind();
             CategoryList.Items.Insert(0, new ListItem("All", "-1"));
             CategoryList.SelectedIndex = CategoryList.Items.IndexOf(CategoryList.Items.FindByValue(defaultCategory.ToString()));
             base.OnLoad(e);
          }         
          
          
       }
    
       private void Search_Click(object sender, EventArgs e)
       {
          Results.DataSource = GetResults(Convert.ToInt32(CategoryList.SelectedValue));
          Results.DataBind();      
       }
       
       private DataTable GetCategories()
       {      
          if (Cache["AllCategories"] != null)
          {
             return (DataTable) Cache["AllCategories"];
          }
    
          SqlConnection connection = new SqlConnection("Data Source=DB;Initial Catalog=Store;User Id=User;Password=PW;");
          string sql = string.Format("SELECT * From Categories");
          SqlCommand command = new SqlCommand(sql, connection);
    
          SqlDataAdapter da = new SqlDataAdapter(command);
          DataTable dt = new DataTable();
    
          da.Fill(dt);
          Cache.Insert("AllCategories", dt, null, DateTime.Now.AddHours(1), System.Web.Caching.Cache.NoSlidingExpiration);
    
          connection.Dispose();
          return dt;
          
       }
       private DataTable GetResults(int categoryId)
       {
          SqlConnection connection = new SqlConnection("Data Source=DB;Initial Catalog=Store;User Id=User;Password=PW;");
          string sql = string.Format("SELECT * FROM Products P INNER JOIN Categories C on P.CategoryId = C.Id WHERE C.Id = {0} OR {0} = -1", categoryId);
          SqlCommand command = new SqlCommand(sql, connection);
          
          SqlDataAdapter da = new SqlDataAdapter(command);
          DataTable dt = new DataTable();
          
          da.Fill(dt);
                
          connection.Dispose();      
          return dt;           
       }
    }
    
More Posts

Our Sponsors

Proudly Partnered With