динамически выбирать строку подключения к активно доступной базе данных в рамках сущности - PullRequest
1 голос
/ 07 ноября 2019

В настоящее время мы используем два сервера с одинаковым именем базы данных и настройками. Я хотел бы взять строку подключения для той, которая активна, или той, к которой я могу легко подключиться. Скажем, например, иногда у нас отключение питания в том месте, где расположен сервер-1, поэтому я могу использовать сервер-2, так как он расположен в другом месте.

Здесь я использую помощник строки подключениякласс в .NET для одного без сущности, но я не могу добиться этого для сущности.

в web.config

<add name="dbAdo1a" connectionString="connection for Server-1" providerName="abc" />
<add name="dbAdo1b" connectionString="connection for Server-2" providerName="abc" />

в C #

public class ConnectionStringHelper
    {
        private static readonly Timer Timer;
        private static readonly ConcurrentDictionary<int, string> BestConnectionStrings;
        private const int LoopCount = 3;
        static ConnectionStringHelper()
        {
            BestConnectionStrings = new ConcurrentDictionary<int, string>();
            for (var i = 1; i <= LoopCount; i++)
            {
                GetQuickOneSync(i);
            }
            Timer = new Timer(Constants.ConnectionRefreshTimeout * 1000);
            Timer.Elapsed += async (sender, e) => await HandleTimerElapsed(sender, e).ConfigureAwait(false);
            Timer.AutoReset = false;
            Timer.Start();
        }


        public static string GetConnectionString(int wareHouseId)
        {
            if (!BestConnectionStrings.ContainsKey(wareHouseId))
            {
                return string.Empty;    // just swallow the error if no connection available
            }

            return BestConnectionStrings[wareHouseId];
        }

        private static async Task HandleTimerElapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                // Find the quickies for each of the connection strings
                var sw = new Stopwatch();
                sw.Start();
                // TODO - three sets of ConnectionStrings for now, make dynamic
                for (var i = 1; i <= LoopCount; i++)
                {
                    await GetQuickOne(i).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {

            }
            finally
            {
                Timer.Start();
            }

            async Task GetQuickOne(int wareHouseId)
            {
                var quickone = string.Empty;
                var quickest = long.MaxValue;
                var sw = new Stopwatch();
                for (var c = 'a'; c <= 'z'; c++)
                {
                    var cs = ConfigurationManager.ConnectionStrings[$"dbAdo{wareHouseId}{c}"]?.ConnectionString;
                    if (string.IsNullOrEmpty(cs)) break;
                    sw.Restart();
                    // open connection and see how long it takes
                    using (var connection = new SqlConnection(cs))
                    {
                        try
                        {
                            await connection.OpenAsync().ConfigureAwait(false);
                        }
                        catch (Exception exception)
                        {
                            continue;   // move on
                        }

                        var milliseconds = sw.ElapsedMilliseconds;
                        if (milliseconds < quickest) // quickest so far?
                        {
                            quickest = milliseconds;
                            quickone = cs;
                        }

                        if (milliseconds <= Constants.ConnectionOkThreshold) // is ok if quickest under the threshold, go no further
                        {
                            break;
                        }
                    }
                }

                BestConnectionStrings[wareHouseId] = quickone;
            }
        }

        private static void GetQuickOneSync(int wareHouseId)
        {
            var quickone = string.Empty;
            var quickest = long.MaxValue;
            var sw = new Stopwatch();
            for (var c = 'a'; c <= 'z'; c++)
            {
                var cs = ConfigurationManager.ConnectionStrings[$"dbAdo{wareHouseId}{c}"]?.ConnectionString;
                if (string.IsNullOrEmpty(cs)) break;
                sw.Restart();
                // open connection and see how long it takes
                using (var connection = new SqlConnection(cs))
                {
                    try
                    {
                        connection.Open();
                    }
                    catch (Exception exception)
                    {
                        continue;   // move on
                    }

                    var milliseconds = sw.ElapsedMilliseconds;
                    if (milliseconds < quickest) // quickest so far?
                    {
                        quickest = milliseconds;
                        quickone = cs;
                    }

                    if (milliseconds <= Constants.ConnectionOkThreshold) // is ok if quickest under the threshold, go no further
                    {
                        break;
                    }
                }
            }

            if (!string.IsNullOrEmpty(quickone))
                BestConnectionStrings[wareHouseId] = quickone;
        }
    }

Я хотел бы сделать тот же вид настройки для структуры сущностей, используя класс System.Data.Entity.DbContext. Где я должен быть в состоянии передать имя строки подключения из веб-конфигурации и проверить, что она активно доступна, и она должна перенаправить на нее.

1 Ответ

1 голос
/ 12 ноября 2019

Исходя из ваших потребностей, я могу предложить решение. Я предполагаю, что вы уже предоставили строку подключения по умолчанию для EF.

Теперь вы можете сделать это следующим образом.

Шаг-1. Создать метод расширения для контекста БД EF:

открытый статический класс ConnectionTools {

public static void CheckConnection(this DbContext source)
{

    if (!source.Database.Exists())
    {
        //TODO: logic to read all your connection string into a list
        string connectionStrings = new List<string>();
        foreach (var connection in connectionStrings)
        {
            //set new connection and check
            source.Database.Connection.ConnectionString = connection;
            if (source.Database.Exists())
            {
            //break loop
            }
        }
    }
}

}

Шаг-2. В вызывающей части сделайте что-то вроде этого:

-

public void SomeOperation()
{
    //assuming you already have your context injected
    var employees = _context.CheckConnection().Employees.ToList();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...