27

When you create a new EntityCollection object, the connection doesn't attempt to open the database until you try and do something with that collection. I need to determine whether or not an Entity collection has a valid connection or not, and I can't find an efficient method of doing it.

Currently I've got this in my code:

var db = new MyEntityCollection();

try
{
     var checkworking = from c in db.Customers select c;
}
catch 
{ 
     ConnectToBackUp();
}

Which is not only horrible code, but very slow since it waits an age to determine whether or not the connection is active before throwing an exception.

I know I can control how long it waits before giving up by using ConnectionTimeout but that's just another ugly hack that makes a bad situation worse.

Surely there's a better way of doing this?

Bob Tway
  • 9,301
  • 17
  • 80
  • 162

6 Answers6

31

Simplest (EF5+):

private bool TestConnection()
{
    var db = new MyEntityCollection();
    int oldTimeOut = db.CommandTimeout;

    try
    {
       db.CommandTimeout = 1;
       db.Connection.Open();   // check the database connection
       return true;
    }
    catch 
    { 
       return false;
    }
    finally 
    {
       db.CommandTimeout = oldTimeOut;
    }
}

Update for EF6:

using System.Data.Common;
...

public bool TestConnection() {
    using (var db = new MyEntityCollection()) {
        DbConnection conn = db.Database.Connection;
        try {
            conn.Open();   // check the database connection
            return true;
        }
        catch {
            return false;
        }
    }
}

Update for EF core 2+:

using (var dbContext = new MyEntityCollection()) {
    return dbContext.Database.CanConnect();
}

or (async):

using (var dbContext = new MyEntityCollection()) {
    return await dbContext.Database.CanConnectAsync();
}
Gianpiero
  • 3,349
  • 1
  • 29
  • 42
11

Are you just wanting to see if the DB connection is valid. If so take a look at the objectcontext.databaseExists().

Stuart King
  • 173
  • 5
  • 3
    Looks good, but unfortunately it throws an error: "A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections." My solution needs to work if there's no DB server *at all* as well as if the connection string is merely wrong. – Bob Tway Feb 23 '11 at 09:21
  • i use DbContext.Database.Exists() – CMS Apr 19 '16 at 20:55
8

Solved this by going around the houses a bit and building a new connection string to test with ADO. Still involves using a try catch but it's a lot faster:

    private bool TestConnection()
    {
        EntityConnectionStringBuilder b = new EntityConnectionStringBuilder();
        ConnectionStringSettings entityConString = ConfigurationManager.ConnectionStrings["MyEntityConnectionString"];
        b.ConnectionString = entityConString.ConnectionString;
        string providerConnectionString = b.ProviderConnectionString;

        SqlConnectionStringBuilder conStringBuilder = new SqlConnectionStringBuilder();
        conStringBuilder.ConnectionString = providerConnectionString;
        conStringBuilder.ConnectTimeout = 1;
        string constr = conStringBuilder.ConnectionString;

        using (SqlConnection conn = new SqlConnection(constr))
        {
            try
            {
                conn.Open();
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
Bob Tway
  • 9,301
  • 17
  • 80
  • 162
  • 1
    Was it the need to set a timeout that made this solution necessary? – froggythefrog Nov 12 '12 at 02:16
  • 4
    @froggythefrog Yes. Otherwise it uses the default, which is too long. – Bob Tway Jul 11 '14 at 08:15
  • I know the pain you are feeling and I am EF6 user, I found this solution to be really simple, elegant and very quick at detecting a drop out. It knocked around about 10 seconds off in my own tests compared to my previous method. – IbrarMumtaz Jun 20 '15 at 11:11
  • for some reason this is not actually changing the Timeout on MySQL (even when using `MySqlConnectionStringBuilder` and `MySqlConnection`. `ConnectionTimeout` DOES equal 1, but the exception is thrown after ~4.9 seconds. – itsho Oct 01 '15 at 10:23
  • I've ended up using `System.Net.Sockets.TcpClient` - see http://stackoverflow.com/a/3631379/426315 – itsho Oct 01 '15 at 10:35
  • This timeout does not actually do anything on EF 6.2+ guys sadly. I got a couple of successful 1 second timeouts but anything more always runs to 24 seconds +. I think MS did a bad job with this one part of EF as the timeout is somewhat unreliable. – Richard Griffiths Dec 04 '19 at 16:57
1

Shouldn't such infrastructure be provided on the network or database server layer? Manually handling connection to backup server looks strange. Moreover it is not possible without waiting for timeout because you must try to open connection to primary server first.

Connection itself is accessible on ObjectContext.Connection. This property should return EntityConnection instance which contains StoreConnection property holding connection to real DB. You can check state of this connection.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
0

In my app user can change connection to database from UI. I use this code to quickly check connection with new connection data:

    public bool CheckAvailable(string sqlServerName, string login, string password)
    {
        var testConnectionString = GetConnectionString(sqlServerName, login, password);

        if (string.IsNullOrWhiteSpace(testConnectionString))
            return false;

        var result = false;

        try
        {
            var testContext = new EntityContext(testConnectionString);

            result = testContext.Database.Exists();
        }
        catch (Exception ex)
        {
            log.Error(exception);
        }

        return result;
    }


    private string GetConnectionString(string sqlServerName, string login, string password)
    {
        var sqlConnectionString = new SqlConnectionStringBuilder
                                 {
                                     DataSource = sqlServerName,
                                     InitialCatalog = ConfigurationProvider.DatabaseName,
                                     UserID = login,
                                     Password = password,
                                     MultipleActiveResultSets = true,
                                     ConnectTimeout = 2 // in seconds
                                 };

        var efConnectionString = new EntityConnectionStringBuilder
                                 {
                                     Provider = ProviderName,
                                     Metadata = ConnectionMetadata,
                                     ProviderConnectionString = sqlConnectionString.ToString()
                                 };

        return efConnectionString.ConnectionString;
    }

ConnectTimeout property of the SqlConnectionStringBuilder type is very important to perform the check lightening fast. If you know, that your database should be available and should response fast you can set this value to 1 (with SQL server installed locally it takes 100-200 milliseconds to check). Do not set 0, because it means no limit and should be avoided in connection strings.

Pavel Shkleinik
  • 6,298
  • 2
  • 24
  • 36
0

I did an extension method to use with EntityFramework Core.

Here's the code:

using Microsoft.EntityFrameworkCore;
using System.Data.Common;

namespace TerminalInventory
{
    public static class ExtensionMethods
    {
        public static bool TestConnection(this DbContext context)
        {
            DbConnection conn = context.Database.GetDbConnection();

            try
            {
                conn.Open();   // Check the database connection

                return true;
            }
            catch
            {
                return false;
            }
        }
    }
}

Now you just have to call:

if (!context.TestConnection())
{
    logger.LogInformation("No database connection. Check the connection string in settings.json. {0}", configuration["connectionString"]);

    return;
}
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480