Click here to Skip to main content
15,906,558 members
Articles / Programming Languages / C#
Article

Server Availability

Rate me:
Please Sign up or sign in to vote.
3.08/5 (8 votes)
29 Jul 2008CPOL2 min read 27.3K   362   22   4
Making generalized database server connection utility

Other Downloads

Introduction

Let me summarize the steps for making a connection through this utility:

  1. Fetch the list of server instances available on LAN.
  2. Give user functionality to select particular server instance name from the list of server instances.
  3. Fetch the list of databases by connecting with the selected server instance.
  4. Give user functionality to give user name and password if there is any to connect with database. (It is needed only when user selects SQL Authentication mode).
  5. Give user functionality to select particular database name from the list.
  6. Connect with the database and give user confirmation that connection has been successfully established.

In Detail

There are mainly three methods to find the list of servers in the local area network:

  1. Using SQLDMO class
  2. Using SQLSMO class
  3. Using general .NET class

Each and every method has its own pros and cons:

1. Using SQLDMO Class

There are more cons than advantages of using the SQLDMO class:

  • It only lists SQL Server 2000 server instances over the LAN.
  • It requires SQLDMO.DLL in specific path C:\Program Files\Microsoft SQL Server\80\Tools\Binn
  • It requires SQLDMO.DLL to be registered under Windows server registry through
    regsvr32 C:\Program Files\Microsoft SQL Server\80\Tools\Binn\SQLDMO.DLL command

You cannot use SQLDMO class directly in your application. For using SQLDMO class, there is a wrapper provided by Microsoft - Interop.SQLDMO.DLL. The wrapper provides you sqldmo wrapper classes to use inside your application.

You can fetch the list of server instances in LAN by using the following code:

C#
 /*
For getting list of server instances name.
*/
//Declaring new object of type SQLServer defined in Interop.SQLDMO.DLL
SQLDMO.SQLServer o_SqlServer;
//Instantiating object o_SqlServer
o_SqlServer = new SQLDMO.SQLServer();
//Declaring namelist object to store list of SQL server instances
SQLDMO.NameList o_List;
//Declaring Application object to fetch list of SQL servers
SQLDMO.Application o_SqlApplication;
//Fetching list of SQL server instances over the LAN
o_List = o_SqlApplication.ListAvailableSQLServers();
//You can access particular server name by using integer index number.
o_List.Item(index);

/*
Fetch list of databases based on server instance name user has selected
*/
List<String> o_DBList = new List<string>();
o_SqlServer.LoginSecure = true;
if (userName == String.Empty)
{
    o_SqlServer.Connect(serverName, null, null);
}
else
{
    o_SqlServer.Connect(serverName, userName, password);
}
foreach (Database o_database in o_SqlServer.Databases)
{
   o_DBList.Add(o_database.Name);
}

2. Using SQLSMO Class

It will give you a list of both SQL Server 2000 and SQL Server 2005.

There are some pitfalls of using SQLSMO which prevent us from using this method. It requires four DLLs along with the solution or setup to carry, to use this method.

  • Microsoft.SqlServer.ConnectionInfo.dll
  • Microsoft.SqlServer.Smo.dll
  • Microsoft.SqlServer.SmoEnum.dll
  • Microsoft.SqlServer.SqlEnum.dll
C#
/*
For getting list of server instances name.
*/
DataTable serverName;
 // get all data sources available in network using SMO
 serverName = SmoApplication.EnumAvailableSqlServers();
 //To access server names
 foreach (DataRow ds in serverName.Rows)
{
    string ds_server_name;
    ds_server_name = ds["Name"].ToString();
}

/*
Fetch list of databases based on server instance name user has selected
*/
// SMO server object
private Server server = new Server("selected-server instance-name");  
foreach (Microsoft.SqlServer.Management.Smo.Database db in selected_ds.Databases)
{
    //To access database name
    String db_name;
    db_name = db.Name
}

3. Using General .NET Class

.NET provides some internal classes that come along with .NET Framework which will give you the list of server instances. Once you get the server, there are some mechanisms which you can use to connect with the server and fetch the list of databases. Once you get the server instance name and database name, it is very easy to connect with that particular selected database in the selected server instance name.

C#
/*
For getting list of server instances name.
*/
System.Data.Sql.SqlDataSourceEnumerator servers = _
System.Data.Sql.SqlDataSourceEnumerator.Instance;
System.Data.DataTable serversTable = servers.GetDataSources();
foreach(DataRow row in serversTable.Rows)
{
    //row[0]=networked computer name
    //row[1]=database server instance name
    //to get full name for connection use following method
    string serverinsance_name;
    serverinsance_name=row[0].ToString() + @"\" + row[1].ToString()
}

/*
Fetch list of databases based on server 
instance name user have selected
*/
//In generalized method you need to make connection 
//with the selected server instance name
//for fetching list of databases
String ml_conxString;
DataTable o_DatabaseList;
if(string.IsNullOrEmpty(UserName.Text))
{
    ml_conxString = "Data Source=" + ServerName.Text.Trim() + ";_
    Integrated Security=True";
}
else
{
    ml_conxString = "Data Source=" + ServerName.Text.Trim() + ";_ 
    Initial Catalog=" + DataBaseName.Text.Trim() + ";_
    User ID = " + UserName.Text.Trim() + ";Pwd = " + Password.Text;
}
using (SqlConnection sqlConx = new SqlConnection(ml_conxString))
{
    sqlConx.Open();
    o_DatabaseList = sqlConx.GetSchema("Databases");
    sqlConx.Close();
}
//To access database name
foreach (DataRow row in o_DatabaseList.Rows)
{
    string ml_databasename;
    ml_databasename = row["database_name"].ToString();
}

//Testing connection is same as we have fetched 
//database list based on server instance name
String ml_conxString;
if(string.IsNullOrEmpty(UserName.Text))
{
    ml_conxString = "Data Source=" + ServerName.Text.Trim() + ";_
    Initial Catalog=" + DataBaseName.Text.Trim() + ";Integrated Security=True";
}
else
{
    ml_conxString = "Data Source=" + ServerName.Text.Trim() + ";_
    Initial Catalog=" + DataBaseName.Text.Trim() + ";_
    User ID = " + UserName.Text.Trim() + ";Pwd = " + Password.Text;
}
SqlConnection sqlConx = new SqlConnection(ml_conxString);
sqlConx.Open();
sqlConx.Close(); 

Summary

There are mainly three methods to find the list of servers in the local area network:

  1. Using SQLDMO class
  2. Using SQLSMO class
  3. Using general .NET class

The best method out of the three is the last one because it does not have any dependency on any external DLL.

History

  • 29th July, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Amultek Software Solutions Pvt Ltd.
India India
Jinal Desai
Sr. Software Engineer
Amultek Software Solutions Pvt Ltd.
Ahmedabad

Comments and Discussions

 
BugProject Missing - Contains Virus Pin
RHBrooks14-Jun-17 11:16
professionalRHBrooks14-Jun-17 11:16 
GeneralAn alternative requires zero configuration and DLL's to make it work... Pin
Member 4373905-Aug-08 11:54
Member 4373905-Aug-08 11:54 
Issues with SQLDMO and Interop.SQLDMO

I've struggled with this concept too. When I deploy databases or updates sometimes I run into problems with SQLDMO because it has to be installed locally, Interop.SQLDMO is better but it too has issues and errors unless you have certain tools installed on the workstation, or server. As I see it there are 2 big problems.

1) Using SQLDMO.DLL or Interop.SQLDMO.dll often fails either because they don't have the SQL tools installed or for security reasons you can't register the dll on the server long enough to use it. So looking up servers on the network using either DLL can be a problem.

2) No matter which DLL's you use, if the application is run on the local Microsoft SQL server, it will always leave out the local name.

Here is how I solved those two part of the problems:

1) I created a class called GetSQLServerList in my project which I have included below. It relies on the ODBC lookup which for has never failed yet, but then all my clients use Windows 2000 and above.

2) To solve the second problem I use a recursive lookup on the "HKLM\Software\Microsoft\Microsoft SQL Server\Instance Names\SQL" key in the registry. I then either use (local) or the computer name in front of the instance names found and add them to my list of servers to display and pass it back to the installer I use. It's a bit cluncky and I really need to add a feature to alphabatize the servers after adding the local instances but you get the gist.

using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace GetSQL
{
    class GetSQLServerList
	{
		[DllImport("odbc32.dll")]
		private static extern short SQLAllocHandle(short hType, IntPtr inputHandle, out IntPtr outputHandle);
		[DllImport("odbc32.dll")]
		private static extern short SQLSetEnvAttr(IntPtr henv, int attribute, IntPtr valuePtr, int strLength);
		[DllImport("odbc32.dll")]
		private static extern short SQLFreeHandle(short hType, IntPtr handle); 
		[DllImport("odbc32.dll",CharSet=CharSet.Ansi)]
		private static extern short SQLBrowseConnect(IntPtr hconn, StringBuilder inString, 
			short inStringLength, StringBuilder outString, short outStringLength,
			out short outLengthNeeded);

		private const short SQL_HANDLE_ENV = 1;
		private const short SQL_HANDLE_DBC = 2;
		private const int SQL_ATTR_ODBC_VERSION = 200;
		private const int SQL_OV_ODBC3 = 3;
		private const short SQL_SUCCESS = 0;
		
		private const short SQL_NEED_DATA = 99;
		private const short DEFAULT_RESULT_SIZE = 1024;
		private const string SQL_DRIVER_STR = "DRIVER=SQL SERVER";

        private GetSQLServerList() { }

		public static string[] GetServers()
		{
			string[] retval = null;
			string txt = string.Empty;
			IntPtr henv = IntPtr.Zero;
			IntPtr hconn = IntPtr.Zero;
			StringBuilder inString = new StringBuilder(SQL_DRIVER_STR);
			StringBuilder outString = new StringBuilder(DEFAULT_RESULT_SIZE);
			short inStringLength = (short) inString.Length;
			short lenNeeded = 0;

			try
			{
				if (SQL_SUCCESS == SQLAllocHandle(SQL_HANDLE_ENV, henv, out henv))
				{
					if (SQL_SUCCESS == SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,(IntPtr)SQL_OV_ODBC3,0))
					{
						if (SQL_SUCCESS == SQLAllocHandle(SQL_HANDLE_DBC, henv, out hconn))
						{
							if (SQL_NEED_DATA ==  SQLBrowseConnect(hconn, inString, inStringLength, outString, 
								DEFAULT_RESULT_SIZE, out lenNeeded))
							{
								if (DEFAULT_RESULT_SIZE < lenNeeded)
								{
									outString.Capacity = lenNeeded;
									if (SQL_NEED_DATA != SQLBrowseConnect(hconn, inString, inStringLength, outString, 
										lenNeeded,out lenNeeded))
									{
										throw new ApplicationException("Unabled to aquire SQL Servers from ODBC driver.");
									}	
								}
								txt = outString.ToString();
								int start = txt.IndexOf("{") + 1;
								int len = txt.IndexOf("}") - start;
								if ((start > 0) && (len > 0))
								{
									txt = txt.Substring(start,len);
								}
								else
								{
									txt = string.Empty;
								}
							}						
						}
					}
				}
			}
			catch (Exception ex)
			{
				//Throw away any error if we are not in debug mode
#if (DEBUG)
				MessageBox.Show(ex.Message,"Acquire SQL Servier List Error");
#endif 
				txt = string.Empty;
			}
			finally
			{
				if (hconn != IntPtr.Zero)
				{
					SQLFreeHandle(SQL_HANDLE_DBC,hconn);
				}
				if (henv != IntPtr.Zero)
				{
					SQLFreeHandle(SQL_HANDLE_ENV,hconn);
				}
			}
	
			if (txt.Length > 0)
			{
				retval = txt.Split(",".ToCharArray());
			}

			return retval;
		}
	}
}



I post this because I think it may help others who have struggled with this as well. I hate to use c# because clients may not have the framework installed, but I don't code much in C++ or well enought yet to convert my code over to that, so my solution works for me but may not work in all cases.

Hope this helps Big Grin | :-D

Lee
Yep that's me.

GeneralNot so general Pin
PIEBALDconsult29-Jul-08 14:36
mvePIEBALDconsult29-Jul-08 14:36 
GeneralRe: Not so general Pin
Member 43739012-Aug-08 15:54
Member 43739012-Aug-08 15:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.