I'm connecting to Firebird Database using C#
I can't find where to check the ConnectionState to the database
If I make new fbConnection inside project it's easy to check it by fbConnection.ConnectionState
but I have made this connection with wizard and it's saved in App.config file
I tried to use System.Configuration.ConfigurationManager but it doesn't have ConnectionState
So how can I check connection state that defined in App.config file.
It seems that you don't understand how this thing (the connection) works or what it is. The ConfigurationManager class just helps you to retrieve the connectionstring (note the word string. It is just a string nothing more).
With this string you can build your real instance of an FBConnection and try to open that connection. Now you could check the ConnectionState.
So
string connectionString = ConfigurationManager.ConnectionStrings["MyConnection"].ConnectionString;
using(FbConnection myConnection = new FbConnection(connectionString))
{
// At this point the myConnection instance is certainly closed so
// it is total useless to check the ConnectionState
myConnection.Open();
// At this point the myConnection instance is certainly opened,
// otherwise you get an exception and your code cannot contine,
// so also here it is useless to check the ConnectionState
// however...
if(myConnection.ConnectionState == ConnectionState.Open)
{
.... do your stuff with the connection.....
}
}
For this code to work you need to have in your App.Config the appropriate section where you store the details required to connect to your database under the symbolic name "MyConnection"
<connectionStrings>
<add name="MyConnection" connectionString="Data Source=localhost;
Database=YourDatabaseFile.fdb;
User=YourUserName;
Password=????whatever???;
Pooling=True" />
</connectionStrings>
So I really have never needed to check the connection state of a connection. The only possible use if this property is when you want to keep a global connection instance and in various part of your code you need to check the ConnectionState before trying to open or close this global instance. This scenario is not the best way to work with a disposable object like the connection that should be opened for the shortest period of time possible and the immediately destroyed (disposed) And exiting from the using block disposes the connection.
Check out this link, it may be useful for you.
https://sourceforge.net/p/firebird/NETProvider/ci/0475d606c597498a99be50393646c1c92d773dd4/tree/examples/ASP.NET/web.config
Related
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();
I'm working on an app that has a live network database and a contingency local database, and it detects whether the live network db is accessible, and, if not, it times out after three seconds, changing the connectionstring to the local contingency database.
Following tips here on SO, I managed to alter the connectionstring on app.config during run time and reload the settings.
This is the method the app calls when a change on the connection string is needed:
public static void ChangeConnectionString(string connectionstring)
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings[0].ConnectionString = connectionstring;
var connectionStrings = config.ConnectionStrings;
foreach (ConnectionStringSettings connectionString in connectionStrings.ConnectionStrings)
{
connectionString.ConnectionString = connectionstring;
}
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
PDV_WPF.Properties.Settings.Default.Save();
PDV_WPF.Properties.Settings.Default.Reload();
//Ensures the configuration is saved and reloaded.
FbConnection.ClearAllPools();
//Closes all currently open connections which might be using the old connection string.
Debug.WriteLine("==========Ran ChangeConnectionString");
Debug.WriteLine("==========FDBConnString is:");
Debug.WriteLine("==========" + PDV_WPF.Properties.Settings.Default.FDBConnString);
After I disconnect my computer form the network, whenever I check the current FDBConnString, it correctly points to the local contingency database. However, on the very next line, when it tries to run a query, I get the following exception:
Inner Exception 1:
IscException: Unable to complete network request to host "dbserver".
Inner Exception 2:
SocketException: Este host não é conhecido //(This host is unknown)
Full exception details: https://pastebin.com/3syLvsQf
It seems that, even after I successfully change the connection string, and successfully reload the application config file, it still tries to open a connection using the old connection string. Even if I call Debug to print the current Properties.Settings.Default.FDBConnString right on the line above the call for FbConnection.Open(), it shows the new string rather than the incorrect, old one.
Any insights on what might be going on?
I found what was the issue.
I am instancing a inherited table adapter from the generated xsd file. When I declare a table adapter on my class it also inherits the connection string stored on app.config at the time of declaration. So it doesn't matter if I change app.config, as the declared table adapter is already stuck with the previous connection string.
So, the solution was, rather than changing the connection string stored on app.config, I just had to change the connection string on the declared table adapter:
tB_STOCKTableAdapter1.Connection.ConnectionString = Properties.Settings.Default.ContingencyDB;
tB_STK_PRODUCTTableAdapter1.Connection.ConnectionString = Properties.Settings.Default.ContingencyDB;
or
tB_STOCKTableAdapter1.Connection.ConnectionString = Properties.Settings.Default.NetworkDB;
tB_STK_PRODUCTTableAdapter1.Connection.ConnectionString = Properties.Settings.Default.NetworkDB;
Both ContingencyDB and NetworkDB are strings stored on app.config as a user-scoped string, which can be changed via a given "Settings" window presented to the user.
I have the following type of code in my data layer, which can be called from a console app, windows app, etc, with the proper connection string being read from the corresponding caller's App.Config file:
public static udsDataset GetDataset(int datasetID)
{
string connectionString =
ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
string sql = #"select * from Dataset WHERE DatasetID=#datasetID";
using (SqlConnection conn = new SqlConnection(connectionString))
{
// Dapper query:
return conn.Query<udsDataset>(sql, new {datasetID } ).First();
}
}
I now want to call this same code from a SQLCLR stored procedure (within the database where these tables exist), where you would typically use a context connection:
using(SqlConnection connection = new SqlConnection("context connection=true"))
{
connection.Open();
// etc etc etc
}
The most obvious approach that comes to mind is to overload the function:
public static udsDataset GetDataset(int datasetID)
{
string connectionString =
ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connectionString))
{
return GetDataset(datasetID, conn);
}
}
public static udsDataset GetDataset(int datasetID, SqlConnection conn)
{
// caller is responsible for closing and disposing connection
string sql = #"select * from Dataset WHERE DatasetID=#datasetID";
return conn.Query<udsDataset>(sql, new {datasetID } ).First();
}
So apps with an App.Config could call the connection-less version and SQLCLR could call the version requiring a SqlConnection.
This "seems ok", but having to write the exact same style of overload for every single similar function makes it feel wrong.
Taking the question (and comments on it) at face-value, why do you need:
the option of passing in an existing connection when calling from a SQLCLR procedure
? You should treat the Context Connection the same as any other connection with regards to Open and Dispose. It sounds like you are thinking that the SqlConnection, when using a Connection String of "Context Connection = true;", needs to be opened only once and then not disposed until completely done, whereas you would Open / Dispose of it several times otherwise. I don't see any reason to have differing behavior in these two scenarios.
All of that aside, how to best handle detecting the change in environment (between Console App and SQLCLR object)? You have two choices, both being probably easier than you are expecting:
Make no changes to the app code, but rely on an additional config file:
You can create a file named sqlservr.exe.Config in the C:\Program Files\Microsoft SQL Server\MSSQL{SqlVersion}.{SqlServerInstanceName}\MSSQL\Binn folder (e.g. C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Binn, where the 11 in MSSQL11 is for SQL Server 2012). The format of this file, as should probably be expected, is as follows:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="CoolioAppDB" connectionString="Context Connection = true;" />
</connectionStrings>
</configuration>
This might be considered "cleaner" code, but does introduce an external dependency that your DBA might be ok with, might dislike but tolerate, or might ask your manager to write you up for ;-).
Make a very minor change to the app code, but don't rely on an additional config file:
You can easily auto-detect whether or not you are currently running in SQL Servers's CLR host by using the IsAvailable property of the SqlContext class. Just update your original code as follows:
string connectionString = "Context Connection = true;"; // default = SQLCLR connection
if (!SqlContext.IsAvailable) // if not running within SQL Server, get from config file
{
connectionString =
ConfigurationManager.ConnectionStrings["CoolioAppDB"].ConnectionString;
}
This usage, by the way, is noted in the "Remarks" section of that linked MSDN page for the IsAvailable property.
We have a need to use an old fashioned ADO database connection in one tiny part of the main entity framework application.
We can manually specify the connection string in this part of code, but given that the connection string is already present in the App.Config this seems redundant.
However when we use the configuration manager to retrieve the connection string, it brings with it all of the metadata stuff that entity framework uses.
This causes an error as ADO doesnt recognise the metadata keyword.
How can I parse this connection string to remove the metadata and just get the plain ADO connection string?
You can get DbConnection instance from DbContext:
var context = new YourDbContext();
var connection = context.Database.Connection;
Of course, you can get connection string from connection, but you don't need thus you can use already existing connection object.
Here is Quick Watch of connection object - as you can see it's simple ADO.NET SqlConnection object with ordinal connection string.
In config file I have Entity Framework connection string with metadata:
<connectionStrings>
<add name="NorthwindEntities"
connectionString="metadata=res://*/Northwind.csdl|res://*/Northwind.ssdl|res://*/Northwind.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=Northwind;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
Below should work :
var efConn = new System.Data.EntityClient.EntityConnectionStringBuilder(efConnection);
string adoConn = efConn.ProviderConnectionString;
I was trying to the same and ended up using this approach:
private static string RemoveEntityFrameworkMetadata(string efConnection)
{
int start = efConnection.IndexOf("\"", StringComparison.OrdinalIgnoreCase);
int end = efConnection.LastIndexOf("\"", StringComparison.OrdinalIgnoreCase);
// We do not want to include the quotation marks
start++;
int length = end - start;
string pureSqlConnection = entityFrameworkConnection.Substring(start, length);
return pureSqlConnection;
}
This may not be the most elegant solution, but it works.
(I also tried Regex but can't get my head around it.)
Looks like SQLite database connection doesn't actually try to open database connection when I call Open() function. Here's a simple test:
var factory = DbProviderFactories.GetFactory("System.Data.SQLite");
connection = factory.CreateConnection();
connection.ConnectionString = "data source=NonExistentDB.db3";
conn.Open();
The above code does not generate any kind of exception. Moreover, the connection state is Open after this. Is there a way to do "Test Connection" that would physically establish a connection with the database?
Change to
connection.ConnectionString = "data source=NonExistentDB.db3;FailIfMissing=True"
Without the last argument, it will simply create a new database if the file is not found.