Checking whether a database is available? - c#

My problem involves checking if I have a valid database connection before reading from the database. If the database is down I'd like to write to a xml file instead. I have the location of the database (if it's up) at runtime so if the database was working I can create a new sqlConnection to it.

Use a typical try...catch...finally structure, and based on the specific exception type and message, decide whether you want to write to xml or not.
try
{
SqlConnection connection = new SqlConnection(DB("Your DB Name"));
connection.Open();
}
catch (Exception ex)
{
// check the exception message here, if it's telling you that the db is not available. then
//write to xml file.
WriteToXml();
}
finally
{
connection.Close();
}

I would just use something like:
using(SqlConnection conn = new SqlConnection(c)) {
conn.Open();
}
It will throw an exception if invalid. You could write to the xml in the exception.

An easy way would be to execute a simple query and see if an error occurs:
For Oracle:
SELECT * FROM DUAL
For SQL Server
SELECT 1
Basicly just some kind of relatively "free" query that will let you know that the database is up and running and responding to requests and your connection hasn't timed out.

You cannot really tell whether the DB is up and running without actually opening a connecting to it. But still, connection might be dropped while you're working with it, so this should be accounted for.

Related

Simple SqlCacheDependency

Almost every tutorial I have read seems to incorrectly setup SqlCacheDependency. I believe they normally mix up the outdated polling method with the query notification method.
Here are two of many examples:
Web Caching with SqlCacheDependency Simplified (non-microsoft)
SqlCacheDependency Class (Microsoft)
Based on my testing, if you are using the broker (MSSQL 2015+) you don't need to make any .config changes nor do you need to make any SqlCacheDependencyAdmin calls (Don't need to define tables, etc).
I simplify just do this...
SqlDependency.Start(connString)
...
queryString = "SELECT ...";
cacheName = "SqlCache" + queryString.GetHashCode();
...
using (var connection = new SqlConnection(connString))
{
connection.Open();
var cmd = new SqlCommand(queryString, connection)
{
Notification = null,
NotificationAutoEnlist = true
};
var dependency = new SqlCacheDependency(cmd);
SqlDataReader reader = cmd.ExecuteReader();
try
{
while (reader.Read())
{
// Set the result you want to cache
data = ...
}
}
finally
{
reader.Close();
}
HostingEnvironment.Cache.Insert(cacheName, data, dependency);
}
(The code that checks if the cache is null or not is not included, as that's all just setup. I just want to show the setting of the cache)
This seems to work without the need to define which tables are involved in the query and make complicated triggers on each table. It just works.
More surprising to me is that the rules for making a query have notification :
Creating a Query for Notification (Can't find documentation newer than 2008) don't seem to apply. I purpose to do a TOP in my SQL and it still works.
For a test, I have it run a query 1000 times involving a table named "Settings". Then I update a value in the table and repeat the query.
I watch the Profiler for any queries involving the word "Settings" and I see the query is executed just 1 time (to set the cache) and then the update statement occurs, and then the query is re-executed one more time (the cache was invalidated and the query ran again)
I am worried that in my 2-3 hours of struggling with the proper way to do this I am missing something and it really is this simple?
Can I really just put any query I want and it'll just work? I am looking for any pointers where I am doing something dangerous/non-standard or any small print that I am missing
var dependency = new SqlCacheDependency(cmd);
when you write query like this you autiomatically define table name in it.Your connection already has db name.
It is non explicit way to do same.
Explicit way to catch exception and to know what went wrong is this.
// Declare the SqlCacheDependency instance, SqlDep.
SqlCacheDependency SqlDep = null;
// Check the Cache for the SqlSource key.
// If it isn't there, create it with a dependency
// on a SQL Server table using the SqlCacheDependency class.
if (Cache["SqlSource"] == null) {
// Because of possible exceptions thrown when this
// code runs, use Try...Catch...Finally syntax.
try {
// Instantiate SqlDep using the SqlCacheDependency constructor.
SqlDep = new SqlCacheDependency("Northwind", "Categories");
}
// Handle the DatabaseNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableNotifications method.
catch (DatabaseNotEnabledForNotificationException exDBDis) {
try {
SqlCacheDependencyAdmin.EnableNotifications("Northwind");
}
// If the database does not have permissions set for creating tables,
// the UnauthorizedAccessException is thrown. Handle it by redirecting
// to an error page.
catch (UnauthorizedAccessException exPerm) {
Response.Redirect(".\\ErrorPage.htm");
}
}
// Handle the TableNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableTableForNotifications method.
catch (TableNotEnabledForNotificationException exTabDis) {
try {
SqlCacheDependencyAdmin.EnableTableForNotifications("Northwind", "Categories");
}
// If a SqlException is thrown, redirect to an error page.
catch (SqlException exc) {
Response.Redirect(".\\ErrorPage.htm");
}
}
// If all the other code is successful, add MySource to the Cache
// with a dependency on SqlDep. If the Categories table changes,
// MySource will be removed from the Cache. Then generate a message
// that the data is newly created and added to the cache.
finally {
Cache.Insert("SqlSource", Source1, SqlDep);
CacheMsg.Text = "The data object was created explicitly.";
}
}
else {
CacheMsg.Text = "The data was retrieved from the Cache.";
}
As documented in https://learn.microsoft.com/en-us/dotnet/api/system.web.caching.sqlcachedependency?view=netframework-4.8 "Using a SqlCacheDependency object with SQL Server 2005 query notification does not require any explicit configuration."
So, the CMD has explicit table names in it, and ADO.net is issuing the correct Service Broker configuration commands for you. When the table is updated, SQL Server posts a Service Broker message saying the table has been updated. When ADO.net validates the CMD it checks the explicit tables in the broker for updates.
This is why the SQlCacheDependency associated CMD must use explicit tables.

System.Data.SqlClient.SqlConnection unexpected result in Open() Method with incomplete ConnectionString

When calling the Open() method on a SqlConnection instance that got passed an incomplete connection string, the method does not throw an exception.
I have created the following example to showcase my problem.
var success = true;
var connectionStrings = new[]
{
"Integrated Security=SSPI;",
"Initial Catalog=awdemo;Integrated Security=SSPI;",
"Data Source=.\\sql2016;Initial Catalog=awdemo;Integrated Security=SSPI;"
};
foreach (var connectionString in connectionStrings)
{
var conn = new SqlConnection(connectionString);
try
{
conn.Open();
}
catch (Exception)
{
success = false;
}
finally
{
conn.Close();
Console.WriteLine($"{connectionString} - Success = {success}");
}
}
Result:
Integrated Security=SSPI; - Success = True
Initial Catalog=awdemo;Integrated Security=SSPI; - Success = True
Data Source=.\sql2016;Initial Catalog=awdemo;Integrated Security=SSPI; - Success = True
I would expect conn.Open() to throw an exception for the first two connection strings, since both of them are incomplete and not valid.
Why is there no exception thrown?
EDIT: as pointed out by Steve in the comments an exception is not thrown because the component connects to my default instance of SQL Server when not providing the server information.
Is there a way to force the SqlConnection component to throw an error on incomplete connection strings? In the application it is possible and allowed to create incomplete connection strings.
There's no practical way to fully validate this. You could have a connection string that has all of the components you expect, including a valid server and database name, but it's the wrong server or wrong database.
So if the expectation is that the class should fail on conn.Open() if the connection string is incorrect (not invalid, but incorrect), you can't completely achieve that.
Does it matter? Imagine a few scenarios, all of which include incorrect connection strings:
The connection string is "blarg!" and so attempting to open the connection throws an exception.
The connection string doesn't contain a server or database name. You can open the connection but when you try to execute some command it fails.
The connection string contains a server and database name, but it's the wrong server or database. It fails for the same reason as 2.
In each of the scenarios, what's the first thing you're going to do? You're going to look at the exception. Regardless of which line throws it, you're going to quickly deduce that you have the wrong connection string.
So a "validation" which can't actually validate the connection string before you open it is just going to add work. If the connection string is wrong, that truly is an "exceptional" condition so it's probably better to just let the code throw an exception where it does. You'll find the problem quickly.
All of that aside, suppose you just really want to be sure that your connection contains a server name and a database name before you try to open it. You could write an extension method like this:
// Maybe give it a better name.
public static void ValidateThatConnectionHasDataSourceAndDatabase(this SqlConnection connection)
{
if (string.IsNullOrEmpty(connection.DataSource))
throw new Exception("The connection has no datasource");
if (string.IsNullOrEmpty(connection.Database))
throw new Exception("The connection has no database");
}
After you create the connection and before you open it, call conn.ValidateThatConnectionHasDataSourceAndDatabase();

Best practice to retrieve error message from database

We have list of error messages in our database table and we fetch these error messages from table when we face some business validation error.
for e.g. If in c# code we find that the calculated risk % is more then allowed value, when use below code
string sError = GetErrorText(6610); // get error message from application cache
DisplayErrorPopup(sError ); // load a popup to display error to user.
Now we have found that there are scenarios where we have to validate stuff few rules from stored Procedure. for e.g. "No active supervisor for worker."
My question is how should we handle this scenario when validation happens in database?
A. Should we return error text "No active supervisor for worker." as out param of SP and pass it to DisplayErrorPopup
OR
B. Return the error id (which is present in table) and then use GetErrorText(834) and then pass the text to DisplayErrorPopup;
My concerns are
1. Is there any industry standard to best practice to handle error messages and texts.
2. Is there any drawback of returning string / varchar from database when we have option of returning number.
Please guide me on this.
I would suggest throwing (raising) a custom error in SQL (you could select the text out of a table if you like) and letting your application catch that error. This will allow you to let your application decide how to handle different errors based on how critical they are.
using (SqlConnection conn = new SqlConnection(connection))
{
SqlCommand sqlCommand = new SqlCommand(query, conn);
sqlCommand.CommandTimeout = timeout;
sqlCommand.CommandType = CommandType.Text;
conn.Open();
object result = sqlCommand.ExecuteScalar();
return result;
}
Running the above inside of a try catch block will allow you to handle your errors more elegantly inside of your C# app
Both your options are equally correct.You can either return an error text from database, or return some error code and display error message based on that code in your application.
Also, try this atricle from codeproject as it has both industry standard and best practice to handle error messages and texts explained in detail.

Programmatically checking a connection string username/password?

I have a customer provided database which is configured with multiple users. Each user has their own username/password etc, which are database logins for SQL Server.
In legacy software, the user is asked for their database and password which is then used to form a connectionstring in code.
I am tasked with replacing a lot of the database code for this, and I need a way of checking a username and password against the database quite quickly. At present, using Linq, if the password is incorrect I eventually get a System.Data.SqlClient.SqlException but this takes quite a while to actually throw (I assume a timeout?).
Does anybody know a good way that I can test my username/password combo against the database quickly?
You could try opening a connection through ADO, with a reduced timeout
SqlConnection conn = new SqlConnection (yourconnectionstring + ";Connection Timeout=1;");
try
{
conn.Open();
conn.Close();
}
catch (SqlException ex)
{
if (ex.Number == 18456)
{
// invalid login
}
}
18456 is SQL Invalid login
The default connection timeout is 15 seconds.
I would argue that you don't want the database (or anything, really) to be able to tell you that the credentials that you've passed in are incorrect. Why? Because that allows for brute force checking. Even introducing a small delay goes a long way in thwarting such attempts.

IDataReader for Generic Access

I'm trying to make a "Helper" class where you simply pass the driver and the parameters and the class does the connection and connection string assembly for you.
I've been using the interfaces from System.Data like IDataReader, IDbConnection.
Now after testing it with MySQL the code is working perfectly but as soon as I point and configure it for SQL Server (Microsoft) it does not return any processed rows. I have done some debugging and the info from the SQL server is appearing in the IDataReader but it seems I can't Iterate over it ?
My Current Code:
Connect Method in Helper Class
try
{
factory = System.Data.Common.DbProviderFactories.GetFactory(driver);
_con = factory.CreateConnection();
_con.ConnectionString = buildConnectionString.ToString();
_con.Open();
}
catch (System.Data.Common.DbException ex)
{
_con = null;
throw ex;
}
catch (Exception ex)
{
_con = null;
throw ex;
}
return _con;
A the moment I'm passing my driver as System.Data.SqlClient for SQL Server and MySql.Data.MySqlClient for MySQL.
System.Data.IDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
while (reader.Read())
{
System.Data.DataRow row = table.NewRow();
// Insert info from Reader into the Row
table.Rows.Add(row);
}
reader.Close();
I suspects it has something to do with how IDataReader is trying to handle the types but can't find any documentation on this as it's working perfectly for MySQL but not for SQL Server? Any Help?
You aren't giving many clues here, since most of the interesting code here is probably around the command setup. If, at execution, it never enters the while (reader.Read()) {...} block, then it is probably TSQL or parameter related (especially nulls, which can easily result in no rows).
Since your data is DataTable-centric and you already have the provider-factory, another possibility here is to use CreateDataAdapter() from the factory, and let the factory worry about the binding of TSQL to a DataTable. Otherwise, treble-check that the TSQL you are providing is valid, sensible, and correctly parameterised.
Ultimately, the Read() loop itself is fine, and is pretty-much what all materialization routines do. It is, for example, very close to how dapper works, and that works fine over a range of databases.

Categories