Issues and Troubleshooting
The following section provides useful information about some issues that may arise after upgrading, and how to solve them.

Microsoft.VisualBasic Uses

Visual Basic 6 inherited features in VB.NET which C# lacks.
Most can be modeled with different constructions to achieve functional equivalence:
    C# equivalent instructions.
    Helper classes.
The rest maps to classes, structures and enumerations on the Microsoft.VisualBasic namespace.
Classes
Enums
Structs
CompareMethod
CallType
SpcInfo
Constants
FileAttribute
TabInfo
Conversion
FirstDayOfWeek
DateAndTime
FirstWeekOfYear
FileSystem
MsgBoxStyle
Information
OpenAccess
Interaction
OpenMode
Strings
OpenShare
VBMath
VariantType
VbStrConv

Microsoft.VisualBasic namespace

The Microsoft.VisualBasic namespace contains types that support the Visual Basic Runtime in Visual Basic. For a complete reference check: Microsoft.VisualBasic.
The Visual Basic Runtime is written entirely in .NET. Please refer to: Visual Basic Runtime.
    "Visual Basic developers have long associated the term "Visual Basic Runtime" with a set of core library files, such as msvbvm60.dll, that are required for Visual Basic 6.0 (and prior) programs to run. In Visual Basic .NET, the term "Visual Basic Runtime" refers to the set of classes in the Microsoft.VisualBasic namespace. The Visual Basic Runtime provides the underlying implementation for global Visual Basic functions and language features such as Len, IsDate, and CStr. And though the new Visual Basic Runtime provides similar facilities as its predecessors, it is entirely managed code (developed in Visual Basic .NET) that executes on the common language runtime. Furthermore, the Visual Basic Runtime is part of the .NET Framework, so it is never something separate that your application has to carry or deploy.".
    "Many of the methods in the Visual Basic Runtime actually use methods and properties from the System namespace (for example, Len() returns String.Length). In some cases you can achieve equivalent results by accessing .NET Framework class library classes directly, but typically you will be more productive using the Visual Basic Runtime when authoring your code. In many cases the Visual Basic Runtime wrappers provide additional functionality that you would have to code yourself if using the System namespace directly. In other cases, such as IsDate, there is no directly equivalent functionality in the System namespace.".
The idea behind the VBUC is to use the minimum possible elements from the Microsoft.VisualBasic namespace. However, you don't have to worry about the remaining ones as they are written in .NET.

Safe and Unsafe Methods Layer

Win-API

The Microsoft Windows application programming interface (Win API):
    Allows calls to Windows OS functions directly.
    All programs interact with the Windows API directly or through some other API.
Many system resources can be managed using this API.
Applications employ them to perform special functions not available from the programming language natively:
    Working with the Windows registry.
    Interacting directly with the user interface.
Implemented in several DLLs that reside on the Windows folder, such as kernel32.dll, advapi32.dll, user32.dll, etc.
These libraries contain what is called unmanaged code in .NET: code that is compiled directly to a binary that can be executed directly in the CPU.
The .NET platform provides the Platform Invocation Services also known as "PInvoke" to interact with unmanaged code from a managed environment.
The VBUC (migration tool) identifies all Windows API calls in the original VB6 code and translates them into their corresponding "PInvoke" signature:
Original VB6 Code
1
Private Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, ByVal lpData As Long, lpcbData As Long) As Long
Copied!
Migrated C# Code
1
[DllImport("advapi32.dll", EntryPoint = "RegQueryValueExA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
2
extern public static int RegQueryValueEx(int hKey, [MarshalAs(UnmanagedType.VBByRefStr)] ref string lpszValueName, int dwReserved, ref int lpdwType, System.IntPtr lpbData, ref int cbData);
Copied!

Code Organization

All the signatures found in the original code are organized by library, resulting in a class per library.
    The code is better organized.
    The original definitions are commented out.
    The required interoperability data-type marshalling is generated.
    The error handling is added for the upgraded API calls.
    The correct data types for pointer-type parameters are generated.
PInvoke Classes
Example for the advapi32 class
1
namespace CompendiaSupport.PInvoke.UnsafeNative {
2
[System.Security.SuppressUnmanagedCodeSecurity]
3
public static class advapi32 {
4
[DllImport("advapi32.dll", EntryPoint = "RegQueryValueExA",
5
CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
6
extern public static int RegQueryValueEx(int hKey,
7
[MarshalAs(UnmanagedType.VBByRefStr)] ref string
8
lpszValueName, int dwReserved, ref int lpdwType, System.IntPtr lpbData, ref int cbData);
9
}
10
}
11
12
namespace CompendiaSupport.PInvoke.SafeNative {
13
public static class advapi32 {
14
public static int RegQueryValueEx(int hKey, ref string
15
lpszValueName, int dwReserved, ref int lpdwType, ref int lpbData, ref int cbData) {
16
int result = 0;
17
GCHandle handle = GCHandle.Alloc(lpbData, GCHandleType.Pinned);
18
try {
19
IntPtr tmpPtr = handle.AddrOfPinnedObject();
20
CompendiaSupport.PInvoke.UnsafeNative.advapi32.RegQueryValueEx(hKey, ref lpszValueName,
21
dwReserved, ref lpdwType, tmpPtr, ref cbData);
22
lpbData = Marshal.ReadInt32(tmpPtr);
23
}
24
finally {
25
handle.Free();
26
}
27
return result;
28
}
29
}
30
}
Copied!

Looking for alternatives?

Microsoft provides an article with a list of Windows API functions and their mappings to .NET.
In the summary, they explicitly say that: "This article identifies the Microsoft .NET Framework version 1.0 or 1.1 APIs that provide similar functionality to Microsoft Win32 functions."
The replacement of API functions with .NET alternatives should be done on a case-by-case basis.
    Imports of the required namespaces are needed.
    Can require modification on the method's logic.
    Creation of new objects
    Calling of the new method(s).
    How to retrieve the return value.
Example for the RegQueryValueEx method:
Win32 function
Description
.NET Framework API
RegQueryValueEx
Retrieves the type and data for a specified value name associated with an open registry key.
Microsoft.Win32.RegistryKey.GetValue

Third Party Components

Component One Classes Extensions

The Compendia projects consume ComponentOne's VSPrinter component.
There is no direct equivalent in the ComponentOne control collection for .NET.
For that matter, extension classes were created:
    To avoid changing logic in the migrated code.
    Maintainability.
The ComponentOne documentation provides information on which components to use for printing and previewing. You can check here: Reports for WinForms Overview
Textually, the ones that serve our purpose are:
    "The C1PrintDocument component provides a rich object model which allows you to create arbitrarily complex documents in code."
    "The integrated components (the C1PrintPreviewControl control and the C1PrintPreviewDialog dialog box) make adding a professional-looking preview to your applications a snap."
C1 Print Classes

Extensions

The extensions classes were created with the required elements that need to be exposed in mind (properties, methods, events, etc.).
A deep investigation of both controls is needed to code equivalent behavior.
Testing is crucial to find and correct bugs.
Fortunately, the VSPrinter control documentation is well detailed. It is the primary source of information.
Let's review the Action property. On the surface, it just assigns a value to the property:
Original VB6 code example
1
m_objVSPrinter1.Action = paStartDoc
2
3
m_objVSPrinter1.Footer = ""
4
strTabs = "^" & CStr(m_objVSPrinter1.PageWidth) & "~"
5
m_objVSPrinter1.MarginLeft = 650 ' was 0 AA: FW 6542
6
m_objVSPrinter1.CurrentY = (m_objVSPrinter1.PageHeight / 2) - 1000
7
m_objVSPrinter1.FontSize = 14
8
m_objVSPrinter1.Table = strTabs & "NO RECORDS FOUND~~"
Copied!
Migrated C# code example
1
m_objVSPrinter1.Action = UpgradeHelpers.Gui.C1PrintDocumentExtension.
2
ActionSettings.paStartDoc;
3
4
m_objVSPrinter1.Footer = "";
5
strTabs = "^" + m_objVSPrinter1.PageWidth.ToString() + "~";
6
m_objVSPrinter1.MarginLeft = 650; // was 0 AA: FW 6542
7
m_objVSPrinter1.CurrentY = Convert.ToInt32((m_objVSPrinter1.PageHeight / 2) - 1000);
8
m_objVSPrinter1.FontSize = 14;
9
m_objVSPrinter1.Table = strTabs + "NO RECORDS FOUND~~";
Copied!
Looking at the documentation, it says the following regarding that property: "Action Property (VSPrinter) Executes an action such as 'StartDoc' or 'EndDoc'.".
Detailed information is given about what it does with each value. The code should work accordingly.
Constant
Value
Description
paNone
0
No effect.
...
...
...
paStartDoc
3
Equivalent to the StartDoc method.
...
...
...
paEndDoc
6
Equivalent to the EndDoc method.
...
...
...
Code snippet with the implementation
1
public ActionSettings Action
2
{
3
get
4
{
5
return _action;
6
}
7
8
set
9
{
10
_action = value;
11
ExecuteDocumentAction();
12
}
13
}
14
private void ExecuteDocumentAction()
15
{
16
switch (Action)
17
{
18
case ActionSettings.paStartDoc:
19
IsDocumentEnded = false;
20
_docStarted = true;
21
InitializeDocument();
22
break;
23
case ActionSettings.paEndDoc:
24
if (_docStarted)
25
{
26
EndDocument();
27
}
28
break;
29
//...more code omitted
30
default:
31
break;
32
}
33
}
Copied!

TrueDbGrid ActiveX Conversion

Description

The VBUC allows converting the TrueDbGrid Actvex to .NET equivalents: C1 TrueDbGrid .NET version or to a Mobilize HelperClass extending the .NET DataGridView.
ComponentOne TrueDBGrid Option

Component One (C1) TrueDBGrid (.NET version)

This conversion targets the Activex migration to their .NET equivalent of ComponentOne.
There are important differences between the Activex and the .NET controls.
TrueDbGrid Columns
For the Activex version, design properties can be accessed from the grid's Columns collection, but in the .NET version, design properties can only be accessed via the DisplayColumn collection of the grid's Splits collection.
In VB6, code like the following:
1
<truedbgrid_instance>.Columns(0).Alignment = dbgGeneral
Copied!
This instruction affects the alignment of all displayed columns in the grid.
On the other hand, when using the C1 version of the TrueDbGrid, that visual property can only be accessed by indicating explicitly the Split containing the column.
The above VB6 column would be converted as follows:
1
//UPGRADE_WARNING: (2081) Getting a DataColumn not from a split has a new behavior. More Information: https://www.mobilize.net/vbtonet/ewis/ewi2081
2
<truedbgrid_instance>.Splits[0].DisplayColumns[this.tgd_Fam.Columns[0]].Style.HorizontalAlignment = C1.Win.C1TrueDBGrid.AlignHorzEnum.General;
Copied!
However, this only affects the columns in Split[0].
.NET Component that extends the DataGridView
All instances of the TrueDbGrid are converted to an extension of the DataGridView. Design limitations of the .NET DataGridView affect the capabilities of the grid.

Getting a DataColumn not from a split has a new behavior

Description
This entry describes the different behavior observed in .NET applications when the TrueDbGrid Activex component is migrated to the C1 TrueDbGrid .NET version when column properties are accessed for getting/setting purposes. This entry covers the UPGRADE_WARNING: (2081) Getting a DataColumn not from a split has a new behavior EWI.
TrueDbGrid Columns
For the Activex version, design properties can be accessed from the grid's Columns collection, but in the .NET version, design properties can only be accessed via the DisplayColumn collection of the grid's Splits collection.
In VB6, code like the following:
1
<truedbgrid_instance>.Columns(0).Alignment = dbgGeneral
Copied!
This instruction affects the alignment of all displayed columns in the grid.
On the other hand, when using the C1 version of the TrueDbGrid, that visual property can only be accessed by indicating explicitly the Split containing the column.
The above VB6 column would be converted as follows:
1
//UPGRADE_WARNING: (2081) Getting a DataColumn not from a split has a new behavior. More Information: https://www.mobilize.net/vbtonet/ewis/ewi2081
2
<truedbgrid_instance>.Splits[0].DisplayColumns[this.tgd_Fam.Columns[0]].Style.HorizontalAlignment = C1.Win.C1TrueDBGrid.AlignHorzEnum.General;
Copied!
However, the above line only affects the columns in Split[0].
If the source grid contains multiple Splits, then the code must be adapted to reflect that situation.
A possibility would be creating a for-each loop to iterate through all Splits in the grid.
1
foreach(C1.Win.C1TrueDBGrid.Split split in <truedbgrid_instance>.Splits)
2
{
3
split.DisplayColumns[0].Style.HorizontalAlignment = C1.Win.C1TrueDBGrid.AlignHorzEnum.General;
4
}
Copied!
This EWI in previous VBUC versions
Starting with the public version of the VBUC 8.2, a for-each loop was created for a limited set of mapped elements of this library, making the code hard to read for an unexperienced user, seeing a lot of new code that was not present in VB6 code. Therefore, starting on VBUC 8.2.50602 this was modified in mappings for this component.

Migration Process

Description

The VBUC allows converting DataAccess related classes like the classic ADODB, Microsoft DAO, RDO or OracleInProc Activex components to .NET equivalents, or even keep them using the original components via COM interop.
Data Access Options
In this post, we are not covering the migration to COM interop for those Activex components.

System.Data.Common and HelperClasses

This solution uses a set of helper objects to provide equivalent behavior in .NET and to encapsulate the ADO.NET machinery required to handle a set of data, more specifically, for the RecordSet object, which is very powerful and flexible in Visual Basic 6 and does not have a direct equivalent in .NET. This approach reduces the manual changes effort to achieve functional equivalence.
The usage of System.Data.Common libraries provide the application with the ability to interact with different Database Manager Systems (such as SQL Server, Oracle, MS Access, etc) through its ADO.NET 2.0 compliant providers with just minimal configuration efforts.
More info is found at Classic ADO Conversion to ADO.NET

ADO.NET Using SqlClient

This optional feature allows the VBUC to convert RDO to plain ADO.NET by using the provider-specific System.Data.SqlClient libraries.
Remarks:
    This approach converts RDO to plain ADO.NET, but it might require several manual changes to achieve functional equivalence.
    Transformations to occurrences of MSRDC will also be applied when selecting this choice.

Known Issues with Upgrade Options

    Do not mix Upgrade options in a single migration even if source components are different: such as ADODB and RDO in the same VB6 project.
    Due to design in VBUC 8.2 and older, choosing System.Data.Common and HelperClasses to migrate ADODB will cause conflicts if SqlClient is chosen for RDO.
If System.Data.Common + Helper is used for some classes and SqlClient for others, the generated code will be hard to modify in order to achieve functional equivalence.
RDO Option
Make sure to be consistent, and just pick either only the SqlClient or only the System.Data.Common + Helper classes options.

Classic ADO Conversion to ADO.NET

Description

The VBUC allows converting the Activex Data Objects (classic ADO) to ADO.NET using the System.Data.Common namespace + Mobilize helper classes. This entry talks about this option.
In the Upgrade Options section of the VBUC tool:
ADODB Option

Active-X Data Objects (ADO)

ADO is an object model for programmatically accessing, editing, and updating data from a wide variety of data sources through the OLEDB system interfaces.
OLEDB is a set of interfaces that expose data from a variety of sources using COM (Component Object Model).
ADO consists of objects and collections. Its main components are Connection, Command, and Recordset.
ADODB Components

ADO to ADO.NET Common

Microsoft provides the System.Data.Common namespace, which contains classes intended to be the base for the implementation of all data providers: ADO.NET.
Many objects from ADO have a counterpart in ADO.NET.
ADODB Objects
As counterpart of the Recordset in the .NET side is the System.Data.DataSet: an object which also holds data retrieved from the database.

Oracle Data provider (ODP.NET) in Migrated projects

Description

Old Visual Basic 6 applications interacting with Oracle databases may rely on the MSDAORA driver (Microsoft OleDb provider for Oracle) in the connection string to establish such communication when that code is converted to .NET. The code migrated to .NET using the Visual Basic Upgrade Companion (VBUC) will keep the same connection string and therefore be using the old MSDAORA driver. But, in .NET we can take advantage of the Oracle Data Provider (ODP.NET) technology developed by Oracle instead.

How VB6 works with MSDAORA

The following VB6 code shows a database connection through the MSDAORA provider:
1
Dim oConn As ADODB.Connection
2
3
Set oConn = New ADODB.Connection
4
5
oConn.ConnectionString = "Provider=MSDAORA.1;Password=" & sPassword & ";User ID = " & sUser & "; Data Source= " & sServer & ";Locales Identifier=1033"
6
7
oConn.Open
Copied!
When this code is converted to .NET using the VBUC the code looks like this:
1
DbConnection oConn = UpgradeHelpers.DB.AdoFactoryManager.GetFactory().CreateConnection();
2
3
oConn.ConnectionString = "Provider=MSDAORA.1;Password=" + sPassword + ";User ID = " + sUser + "; Data Source= " + sServer + ";Locales Identifier=1033";
4
5
//UPGRADE_TODO: (7010) The connection string must be verified to fullfill the .NET data provider connection string requirements. More Information: https://www.mobilize.net/vbtonet/ewis/ewi7010
6
7
oConn.Open();
Copied!
The ADODB component is migrated to ADO.NET using System.Data.Common and helper classes.
As you can see, the migrated application is still using the MSDAORA provider.

Using a Native ODP for .NET

If your final goal is taking full advantage of the .NET technology, you may want to replace that provider for the ODP.NET developed by Oracle. In this case, you need to go to the Oracle Data Provider .NET download page and choose the required version of this .NET component.
After installing and configuring the ODP.NET component on your machine you will have to make some minor adjustments to the migrated code:
Add the Oracle.DataAccess.Client factory
Mobilize helper classes use a DBProviderFactory to create the right ADO.NET object according to the database connection provider in use:
    OleDb providers will use the System.Data.OleDb namespace. This is valid for MS-Access files and any OleDb provider like the MSDAORA one.
    ODBC providers will use the System.Data.ODBC namespace.
    SqlServer can use the System.Data.SqlClient namespace
    Oracle providers for .NET will use the Oracle.DataAccess.Client namespace that comes with the ODP.NET installer. If this assembly is not installed, an exception will raise at runtime.
To use the Oracle.DataAccess.Client, find the method LoadDefaultFactorySettings that comes in the AdoFactoryManager class from the UpgradeHelpers.DB.Essentials helper project and uncomment the line:
1
factorySection.Add("Oracle", new FactoryConfigurationElement("Oracle", "Oracle.DataAccess.Client", DatabaseType.Oracle, false));
Copied!
and comment out this line:
1
factorySection.Add("Oracle", new FactoryConfigurationElement("Oracle", "System.Data.OracleClient", DatabaseType.Oracle, false));
Copied!
So, this method should look like this:
1
private static void LoadDefaultFactorySettings(Dictionary<string, FactoryConfigurationElement> factorySection)
2
3
{
4
factorySection.Add("Access", new FactoryConfigurationElement("Access", "System.Data.OleDb", DatabaseType.Access, false));
5
factorySection.Add("SQLServer", new FactoryConfigurationElement("SQLServer", "System.Data.SqlClient", DatabaseType.SQLServer, false));
6
7
//New Changes
8
factorySection.Add("Oracle", new FactoryConfigurationElement("Oracle", "Oracle.DataAccess.Client", DatabaseType.Oracle, false));
9
//factorySection.Add("Oracle", new FactoryConfigurationElement("Oracle", "System.Data.OracleClient", DatabaseType.Oracle, true));
10
factorySection.Add("ODBC", new FactoryConfigurationElement("ODBC", "System.Data.Odbc", DatabaseType.Access, false));
11
}
Copied!
With these changes, any ADO.NET object (DBCommands, DBConnections, etc) created using the UpgradeHelpers.DB.AdoFactoryManager.GetFactory() will be instantiated using the types defined in the Oracle.DataAccess.Client namespace.
Another approach instead of modifying Mobilize helper classes consists in using the App.Config file of the new .NET project:
1
<?xml version="1.0" encoding="utf-8"?>
2
<configuration>
3
<configSections>
4
<section name="AdoFactories" type="UpgradeHelpers.DB.AdoFactoriesConfigurationSection, UpgradeHelpers.DB.Essentials" allowExeDefinition="MachineToApplication" allowLocation="true" />
5
<section name="AdoIdentityColumns" type="UpgradeHelpers.DB.AdoIdentityColumnsConfigurationSection, UpgradeHelpers.DB.Essentials" allowExeDefinition="MachineToApplication" allowLocation="true" />
6
</configSections>
7
<connectionStrings>
8
</connectionStrings>
9
<AdoFactories>
10
<!--
11
12
The following section declares some of the most common factories. It can be modified in order to accomplish your needs.
13
The factory declaration with the "isdefault" attribute set to true will be used by the upgraded application as the current provider factory.
14
15
The database type attribute can take one of the following values
16
* SQLServer: when the application interacts wiht Ms SQL Server
17
* Oracle: when the application interacts wiht Oracle
18
* Access: when the application interacts wiht Ms Access
19
* Undefined: when none of the previous is being used
20
21
-->
22
<Factories>
23
**<add name="SQLServer" factorytype="System.Data.SqlClient" isdefault="false" databasetype="SQLServer" />
24
<!-- MS SQL Server -->
25
<add name="Oracle" factorytype="System.Data.OracleClient" isdefault="true" databasetype="Oracle" />
26
<!-- Oracle -->
27
<add name="Oledb" factorytype="System.Data.OleDb" isdefault="false" databasetype="Access" />
28
<!-- Any database through Oledb -->
29
<add name="ODBC" factorytype="System.Data.Odbc" isdefault="false" databasetype="Access" />**
30
<!-- Any database through ODBC -->
31
</Factories>
32
</AdoFactories>
33
<AdoIdentityColumns>
34
</AdoIdentityColumns>
35
</configuration>
Copied!
In the factories section, you can define the factory to use for each type of Database. If the application uses an Oracle driver, just change the isdefault attribute to true and set to false the isdefault attributes of all other factories.
Correct the Connection String
As illustrated in the VB6 code above, the connection string is using an OLEDB provider (MSDAORA), so we need to change that string to send the parameters required by the ODP.NET provider:
1
string conStr = "Data Source=(DESCRIPTION=(CID=GTU_APP)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST="+ sServer + ")(PORT="+ sPort + ")))(CONNECT_DATA=(SID="+ sSID + ")(SERVER=shared)))";
2
3
conStr = conStr + ";" + "User Id=" + sUser + ";Password=" + sPassword;
4
5
DbConnection oConn = UpgradeHelpers.DB.AdoFactoryManager.GetFactory().CreateConnection();
6
7
oConn.ConnectionString = conStr;
8
9
oConn.Open();
Copied!

ADO Connection object

Description
ADO Connection objects are converted to their .NET System.Data.Common namespace equivalent. However, unlike ADO, .NET allows using different factories or methods to establish a Database Connection. This article is valid for conversion projects using the "ADODB to ADO.NET using System.Data.Common and helper classes" upgrade options of the Visual Basic Upgrade Companion (VBUC) tool.
ADODB Option
System.Data.Common.DBConnection
System.Data.Common.DBConnection is the .NET equivalent for the ADODB.Connection object. It's an abstract class that defines the core behavior of database connections and provides a base class for database-specific connections.
.NET Database providers (DBProvider for short) offer their own implementation of the DBConnection class to establish a Database connection that allows working with specific data sources.
A DBProvider is a set of classes and methods that implement interfaces or abstract classes defined in System.Data.Common: System.Data. OLEDB, System.Data.Odbc and System.Data.SqlClient are Dbproviders defined in the .NET Framework.
The next VB6 code shows how typically a Connection to a DB is established using classic ADO.
1
Dim conn As ADODB.Connection
2
Set conn = New ADODB.Connection
3
4
conn.ConnectionString = "valid connection string replaced"
5
conn.CommandTimeout = 900
6
conn.Open
7
<some other code>
8
conn.Close
Copied!
C# code
1
DbConnection conn = UpgradeHelpers.DB.AdoFactoryManager.GetFactory().CreateConnection();
2
3
conn.ConnectionString = "valid connection string replaced";
4
//UPGRADE_TODO: (7010) The connection string must be verified to fullfill the .NET data provider connection string requirements. More Information: https://www.mobilize.net/vbtonet/ewis/ewi7010
5
conn.Open();
6
<some other code>
7
UpgradeHelpers.DB.TransactionManager.DeEnlist(conn);
8
conn.Close();
Copied!
1
DbConnection conn = UpgradeHelpers.DB.AdoFactoryManager.GetFactory().CreateConnection();
Copied!
The above statement creates a DBConnection object using the default factory:
    1.
    ODBC (System.Data.ODBC namespace)
    2.
    OLEDB (System.Data.OLEDB).
    3.
    SqlClient (System.Data.SqlClient)
    4.
    External factories like the Oracle.DataAccess.Client
The VBUC sets OLEDB as the default factory, but it can be changed in the AdoFactoryManager.LoadDefaultFactorySettings() method.
In this way, the Provider can be changed at any time and only the connection string should be modified.
VB6 applications converted into .NET using the VBUC tool define a connection string that uses an OLEDB provider. By changing the Default factory in the AdoFactoryManager.LoadDefaultFactorySettings() method and the connection string, the migrated application can connect to a SqlSever Database using a native client (System.Data.SqlClient) instead of an OLEDB driver (as it did in VB6), gaining performance (additional changes may be needed in migrated code to deal with Stored Procedures or other database-related topics).
1
conn.ConnectionString = "valid connection string replaced";
2
//UPGRADE_TODO: (7010) The connection string must be verified to fullfill the .NET data provider connection string requirements. More Information: https://www.mobilize.net/vbtonet/ewis/ewi7010
3
conn.Open();
Copied!
The Connection string is not changed by the VBUC, and it may need to be revised, since in .NET connection strings may be different.
1
UpgradeHelpers.DB.TransactionManager.DeEnlist(conn);
2
conn.Close();
Copied!
As the DBConnection object is closed, the TransactionManager.DeEnlist() method, injected by the VBUC, determines if there's a Transaction linked to the connection object and de-enlists it. If there's no Transaction, this method will do nothing.

Known Issues

ADO Connection.Execute to populate a recordset and executing Insert, Update or Delete operations
Description
This entry describes the scenario when the ADODB.Connection.Execute method is used for both populating a recordset and executing update/delete/insert operations when the affected record count is needed.
The issue
VB6 code
1
Dim conn As ADODB.Connection
2
conn.ConnectionString = "valid-connection-string"
3
conn.Open
4
Dim rs As ADODB.Recordset
5
Set rs = New ADODB.Recordset
6
Dim i As Long
7
8
Set rs = conn.Execute("Insert into TableTest (ID,Options,Name) Values(1,'1','Test')", i)
9
Set rs = conn.Execute("Insert into TableTest (ID,Options,Name) Values(2,'2','Test2')", i)
10
11
Set rs = conn.Execute("Update TableTest Set ID=3 Where Options='2'", i)
12
MsgBox i
13
Set rs = conn.Execute("Delete from TableTest Where Options = '2'", i)
14
MsgBox i
Copied!
C# code
1
DbConnection conn = null;
2
conn.ConnectionString = "valid-connection-string";
3
//UPGRADE_TODO: (7010) The connection string must be verified to fullfill the .NET data provider connection string requirements. More Information: https://www.mobilize.net/vbtonet/ewis/ewi7010
4
conn.Open();
5
ADORecordSetHelper rs = new ADORecordSetHelper("");
6
int i = 0;
7
rs = ADORecordSetHelper.Open("Insert into TableTest (ID,Options,Name) Values(1,'1','Test')", conn, out i, "");
8
rs = ADORecordSetHelper.Open("Insert into TableTest (ID,Options,Name) Values(2,'2','Test2')", conn, out i, "");
9
10
rs = ADORecordSetHelper.Open("Update TableTest Set ID=3 Where Options='2'", conn, out i, "");
11
MessageBox.Show(i.ToString(), Application.ProductName);
12
rs = ADORecordSetHelper.Open("Delete from TableTest Where Options = '2'", conn, out i, "");
13
MessageBox.Show(i.ToString(), Application.ProductName);
Copied!
In this scenario, the Open() method will not return the real number of rows affected by the insert/delete method. The ADORecordsetHelper uses a DataAdapter.Fill() method to populate the underlying dataset, but it does not include the rows affected by statements that do not return rows (like Update/Delete statements).
Alternatives
    1.
    Add Select @@RowCount to the Insert/Update SQL sentence to execute.
    Note: This is valid for SQLServer DBMS. For Oracle you can try SQL%ROWCOUNT.
    1
    rs = ADORecordSetHelper.Open("Update TableTest Set ID=3 Where Options='2'; Select @@RowCount", conn, out i, "");
    2
    i = rs.Tables[0]; //<-- this will have the @@RowCount value
    3
    MessageBox.Show(i.ToString(), Application.ProductName);
    4
    5
    rs = ADORecordSetHelper.Open("Delete from TableTest Where Options = '2'; Select @@RowCount", conn, out i, "");
    6
    i = rs.Tables[0]; //<-- this will have the @@RowCount value
    7
    MessageBox.Show(i.ToString(), Application.ProductName);
    Copied!
    Bonus: @@RowCount not working? Check this for additional info about how @@RowCount works in nested statements.
    Original code (nested SQL Statements):
    1
    INSERT INTO #temptable (...) SELECT a,b..,n FROM TABLE1 where param1=x1 and param2=x2
    2
    IF @@ROWCOUNT = 0
    3
    INSERT INTO #temptable (...) SELECT a,b..,n FROM TABLE1 where param1=y1 and param2=x2
    4
    IF @@ROWCOUNT = 0
    5
    INSERT INTO #temptable (...) SELECT a,b..,n FROM TABLE1 where param1=z1 and param2=x2
    Copied!
    Corrected Code (nested SQL Statements):
    Because the @@rowcount can only be checked once, the above script will skip one insert but then execute the next one. You must properly nest the IFs:
    1
    INSERT INTO #temptable (...) SELECT a,b..,n FROM TABLE1 where param1=x1 and param2=x2
    2
    IF @@ROWCOUNT = 0
    3
    begin
    4
    INSERT INTO #temptable (...) SELECT a,b..,n FROM TABLE1 where param1=y1 and param2=x2
    5
    IF @@ROWCOUNT = 0
    6
    begin
    7
    INSERT INTO #temptable (...) SELECT a,b..,n FROM TABLE1 where param1=z1 and param2=x2
    8
    end
    9
    end
    Copied!
    2.
    Change the generated code
    C# suggestion
    1
    DbConnection conn = null;
    2
    conn.ConnectionString = "valid-connection-string";
    3
    //UPGRADE_TODO: (7010) The connection string must be verified to fullfill the .NET data provider connection string requirements. More Information: https://www.mobilize.net/vbtonet/ewis/ewi7010
    4
    conn.Open();
    5
    ADORecordSetHelper rs = new ADORecordSetHelper("");
    6
    int i = 0;
    7
    rs = ADORecordSetHelper.Open("Insert into TableTest (ID,Options,Name) Values(1,'1','Test')", conn, out i, "");
    8
    rs = ADORecordSetHelper.Open("Insert into TableTest (ID,Options,Name) Values(2,'2','Test2')", conn, out i, "");
    9
    10
    string str = "Update TableTest Set ID=3 Where Options='2'";
    11
    if(str.Trim().StartsWith("delete", StringComparison.InvariantCultureIgnoreCase) || str.Trim().StartsWith("update", StringComparison.InvariantCultureIgnoreCase))
    12
    {
    13
    DbCommand cmd = UpgradeHelpers.DB.AdoFactoryManager.GetFactory().CreateCommand();
    14
    cmd.Connection = conn;
    15
    UpgradeHelpers.DB.DbConnectionHelper.ResetCommandTimeOut(cmd);
    16
    UpgradeHelpers.DB.TransactionManager.SetCommandTransaction(cmd);
    17
    i = cmd.ExecuteNonQuery();
    18
    }
    19
    else
    20
    {
    21
    rs = ADORecordSetHelper.Open(str, conn, out i, "");
    22
    }
    23
    MessageBox.Show(i.ToString(), Application.ProductName);
    24
    25
    str = "Delete from TableTest Where Options = '2'";
    26
    if(str.Trim().StartsWith("delete", StringComparison.InvariantCultureIgnoreCase) || str.Trim().StartsWith("update", StringComparison.InvariantCultureIgnoreCase))
    27
    {
    28
    DbCommand cmd = UpgradeHelpers.DB.AdoFactoryManager.GetFactory().CreateCommand();
    29
    cmd.Connection = conn;
    30
    UpgradeHelpers.DB.DbConnectionHelper.ResetCommandTimeOut(cmd);
    31
    UpgradeHelpers.DB.TransactionManager.SetCommandTransaction(cmd);
    32
    i = cmd.ExecuteNonQuery();
    33
    }
    34
    else
    35
    {
    36
    rs = ADORecordSetHelper.Open(str, conn, out i, "");
    37
    }
    38