Page tree

Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.

This article was originally published in .NET Developer Journal under the title "Five Pillars of Visual Basic Retirement" in November 2006.



titleTable of Contents
Table of Contents



In the past, Visual Basic (VB) upgrades were fairly painless and inexpensive because Microsoft made new versions of VB backward compatible, but things are different this time. An upgrade to .NET brings with it a radical shift in terms of architecture, design, deployment, features, and tools. The upgrade will be even more challenging if you decide to move from the forgiving VB compiler to the rigorous C# compiler. Confronted with declining vendor and community support and major migration challenges, BMW Financial Services in Dublin, Ohio set out to define a strategy that would allow us to adopt C# in an efficient and deliberate manner. Our objectives were to minimize disruption and costs and leverage the momentum of the platform change to move our capabilities forward. This article presents our strategy and some of the experiences we are encountering along the way.

"We knew that taking our business critical systems through such a huge transformation would be challenging. We needed an approach that not only minimized cost and ensured quality but would insulate our business from disruption. We built new architecture frameworks as a part of the project, and the Promula translation tools were an essential part of our strategy. These tools, tuned by Promula to our specifications, have given us the planning flexibility we needed to incorporate the migration into our release process. To this point we have migrated about 50% of our portfolio, with few of our business users even knowing we were doing it. We are on schedule and on budget to complete the migration by March 2008."

- Jeff Haskett,
The General Manager of Applications Development
Group for BMW Financial Services of North America.


On the Road to .NET

BMW Financial Services is committed to Microsoft technologies and has used Visual Basic (VB) as the mainstay of application development for over ten years. Our applications began taking shape in 1994, even as VB itself was still growing up. In early 2000, I was hired to manage the newly established system architecture team. We found ourselves facing a wide variety of architectures and coding styles. Although we are almost purely a VB shop, we had several different application frameworks and our code employed a number of different "standards" for common tasks. We also used over one hundred different third-party COM components. Our intent was to standardize our architectures and to implement more intelligent code reuse, but such changes are expensive and building a case purely on the basis of architecture goodness is near impossible.


We planned three application frameworks to help us migrate: one for desktop, one for web, and one for batch jobs. We fondly refer to these frameworks as DesktopCAFE, WebCAFE, (CAFÉ stands for Common Application Framework for the Enterprise), and BEEF (Batch Environment Execution Framework). Each of these frameworks leverages .NET and the Microsoft Application Blocks / Enterprise library to provide architecture support for quickly assembling robust, modular applications.


Choosing a .NET Language

It was mid-2002, and our first major .NET system was on the critical path of a huge CRM implementation. One of the questions blocking progress was: what language to use? We had been a one-language shop from day one, and it worked for us. We absolutely wanted to standardize on one language rather than support many. We knew all .NET languages have their strengths and weaknesses; but, they are similar in structure and use similar tools; they compile to the same intermediate language, and they use the same runtime engine. We also knew language decisions can become mired in controversy, but we could not let the language decision languish. We conducted a brief language evaluation and looked at the pros and cons of the different options (VB.NET, C#, Managed C++, Jscript, etc.) Within a few days, we settled on C#, published our findings in a brief language selection statement, and got on with the work of adopting .NET. Some of the reasons why we chose C# are listed below:

  1. We felt C# was more mature in terms of sample code, tools (such as NDoc and NUnit), and published books and articles.
  2. Microsoft seemed to be using C# internally, so we figured it was more mature and tested.
  3. We liked the strictness of C# in terms of strong typing, error handling, and case-sensitivity
  4. We did not think VB.Net was that much easier than C#.
  5. We liked that C# had been submitted to ECMA as a standard
  6. We liked that C# was similar to C++ and Java.
  7. We felt the C# community would be large, lively, and strong in the long term.

By the end of 2003, we had published the Batch Architecture Strategy, the WebCAFE adoption strategy and had also deployed a very basic version of DesktopCAFE. Also in 2003, our service-oriented middle tier grew at frightening pace. In our haste to provide an easy-to-use service framework for the CRM project, we put very little governance around the creation of new services. This was both good and bad. On the good side: the C# middle tier provided a ready alternative to writing more VB. On the not so good side, it was the only alternative; and we soon had a library of over 100 services of questionable purpose.


These five processes interact to reduce the cost and increase quality and speed of the .NET adoption effort. For example, an investment in Translation capability will result in higher quality C# code that is more correct and ready for Rearchitecting. An investment in Retooling will result in more repeatable translations, builds, and deployments, as well as smoother adoption of new architectures and tools. Rearchitecting will yield more complete specifications for Translation and a solid target for migrated applications. Testing will ensure that other migration processes are working properly. Managing coordinates and balances these activities to make the most efficient use of program resources. Figure 1 shows how these processes relate to each other.

Image AddedImage Removed

Figure 1: Relationships among the Five Pillars of Migration


  • The replacement of VB On Error Goto with Structured try-catch exception handling,
  • The replacement of a legacy ADO wrapper function (ExecProc) with a call to our standard ADO.NET data access component (DataMeister). Note that DataMeister is a slightly modified version of the SQLHelper application block.
  • The translation requires stored proc parameters information to setup the SqlParameter array. The translation tool is able to determine the names of proc parameters dynamically at translation time – a huge time saver.


Code Block
titleOriginal VB code
Public Function GetLessee(Acct As String) As String
    'Returns name of accountholder, if no last name then use company name.
    If gbErrorTrapOn Then On Error GoTo ErrorTrap
    Dim rs As ADODB.Recordset
    If ExecProc("cash", "cash_GetLessee", rs, -1, Acct, gsCountry) Then
        If Not rs.EOF Then
            If Len(Trim(rs!last_nme)) > 0 Then
                GetLessee = IIf(IsNull(rs!first_nme), "", Trim(rs!first_nme)) & " " & _
                IIf(IsNull(rs!last_nme), "", Trim(rs!last_nme))
                GetLessee = IIf(IsNull(rs!company_nme), "", Trim(rs!company_nme))
            End If
        End If
        GetLessee = "Unavailable"
    End If
    Exit Function
        ShowError("GetLessee", Err.Number, Err.Description)
End Function
Code Block
titleGenerated C# code
public static string GetLessee(string Acct)
    string GetLessee = "";
    // Returns name of accountholder, if no last name then use company name.
    SqlParameter[] sqlParms = new SqlParameter[2];
        SqlDataReader rs = null;
        sqlParms[0] = DesktopDataMeister.SetProcParameter("@account_no",
        ParameterDirection.Input, false, Acct.ToString(), 10);
        sqlParms[1] = DesktopDataMeister.SetProcParameter("@country_cde",
        ParameterDirection.Input, false, Cash_Globals.gsCountry.ToString(), 3);
        rs = DesktopDataMeister.ExecuteReader(DesktopConfigurator.EDBServer,
        CommandType.StoredProcedure, "Cash.dbo.cash_GetLessee", sqlParms);
        if (rs.Read())
            if ((rs["last_nme"].ToString().Trim()).Length > 0)
                GetLessee = (VBNET.Information.IsDBNull(rs["first_nme"]) ? "" :
                rs["first_nme"].ToString().Trim()) + " "
                + (VBNET.Information.IsDBNull(rs["last_nme"]) ? "" :
                GetLessee = (VBNET.Information.IsDBNull(rs["company_nme"]) ? "" :
            GetLessee = "Unavailable";
        return GetLessee;
    catch (Exception exc)
        ShowError("GetLessee", ABCFSException.MapExceptionToErrNumber(exc),

Listing 1. Sample Translation produced by the Promula VB-to-C# Translation System


Mark Juras is president of Great Migrations LLC, a technology solutions provider that specializes in helping people migrate their software applications from one platform to another via translation from one programming language to another. At the time of writing this article, Mark was System Architecture Manager for BMW Financial Services of North America.