Чи є аналізатор рядків з'єднання в C #?


181

У мене є рядок підключення, і я хочу мати можливість зазирнути, наприклад, "Джерело даних". Чи є аналізатор чи мені потрібно шукати рядок?

Відповіді:


305

Так, є System.Data.Common.DbConnectionStringBuilderклас.

Клас DbConnectionStringBuilder забезпечує базовий клас, з якого виводяться сильно набрані будівельники струнних з'єднань (SqlConnectionStringBuilder, OleDbConnectionStringBuilder тощо). Конструктори рядків підключення дозволяють розробникам програмно створювати синтаксично правильні рядки з'єднання та аналізувати та відновлювати існуючі рядки з'єднання.

Цікавими підкласами є:

System.Data.EntityClient.EntityConnectionStringBuilder
System.Data.Odbc.OdbcConnectionStringBuilder
System.Data.OleDb.OleDbConnectionStringBuilder
System.Data.OracleClient.OracleConnectionStringBuilder
System.Data.SqlClient.SqlConnectionStringBuilder

Наприклад, щоб "виглянути джерело даних" з рядка з'єднання SQL-сервера, ви можете зробити:

var builder = new SqlConnectionStringBuilder(connectionString);
var dataSource = builder.DataSource;

6
Базовий клас DbConnectionStringBuilderмає загальні функції обробки, які можна використовувати без використання підкласів:if (builder.TryGetValue("Password", out var pwd)) { string decrypted = SomehowDecrypt(pwd); builder["Password"] = decrypted; }
Олів'є Якот-Дескомбс

41

Є постачальник струнні певної зв'язку будівельники з різних провайдерів , як SqlConnectionStringBuilder, MySqlConnectionStringBuilder, і SQLiteConnectionStringBuilderт.д. ( до жаль , не існує загальнодоступного інтерфейсу від MS на цей раз). В іншому випадку у вас є DbProviderFactory.CreateConnectionStringBuilder, який дасть вам альтернативний спосіб написати це провайдер-агностический спосіб. Вам потрібно буде вказати провайдера у конфігураційному файлі та мати правильну версію dll. Наприклад,

var c = "server=localhost;User Id=root;database=ppp";
var f = DbProviderFactories.GetFactory("MySql.Data.MySqlClient"); //your provider
var b = f.CreateConnectionStringBuilder();
b.ConnectionString = c;
var s = b["data source"];
var d = b["database"];

Я колись писав ручний розбір для себе, який не доставляв мені ніяких проблем. Це було б тривіально розширити це, щоб надати інформацію про інші параметри (зараз це лише для простих речей, таких як ім'я db, джерело даних, ім'я користувача та пароль). Ось так чи так:

static readonly string[] serverAliases = { "server", "host", "data source", "datasource", "address", 
                                           "addr", "network address" };
static readonly string[] databaseAliases = { "database", "initial catalog" };
static readonly string[] usernameAliases = { "user id", "uid", "username", "user name", "user" };
static readonly string[] passwordAliases = { "password", "pwd" };

public static string GetPassword(string connectionString)
{
    return GetValue(connectionString, passwordAliases);
}

public static string GetUsername(string connectionString)
{
    return GetValue(connectionString, usernameAliases);
}

public static string GetDatabaseName(string connectionString)
{
    return GetValue(connectionString, databaseAliases);
}

public static string GetServerName(string connectionString)
{
    return GetValue(connectionString, serverAliases);
}

static string GetValue(string connectionString, params string[] keyAliases)
{
    var keyValuePairs = connectionString.Split(';')
                                        .Where(kvp => kvp.Contains('='))
                                        .Select(kvp => kvp.Split(new char[] { '=' }, 2))
                                        .ToDictionary(kvp => kvp[0].Trim(),
                                                      kvp => kvp[1].Trim(),
                                                      StringComparer.InvariantCultureIgnoreCase);
    foreach (var alias in keyAliases)
    {
        string value;
        if (keyValuePairs.TryGetValue(alias, out value))
            return value;
    }
    return string.Empty;
}

Для цього вам не потрібно нічого особливого в конфігураційному файлі або взагалі будь-який dll. Containsв Whereпункті важливо лише в тому випадку, якщо вам потрібно обійти погано відформатовані рядки з'єднання, наприклад, server = localhost;pp;де ppнічого не додається. Щоб поводитись як звичайні будівельники (які вибухнуть у цих випадках), змініть Whereна

.Where(kvp => !string.IsNullOrWhitespace(kvp))

@Icarus насправді не є ключовим словником порівняння StringComparer.InvariantCultureIgnoreCase. Дивіться ToDictionaryперевантаження
nawfal

1
Так, ти маєш рацію, я просто написав швидкий тест. Видалить мій оригінальний коментар, оскільки він неправильний.
Ікар

3
Ваш метод GetValue () не працюватиме в тих випадках , коли, наприклад, користувач має ';'або '='в їх пароль. Я написав подібну реалізацію і дізнався, що це не дуже важко. Боже, розбір рядків підключення насправді набагато складніше, ніж я думав!
Філіп Атц

@Joshua Я сподіваюся, що ви говорите про частину ручного розбору. Будь ласка, прийміть тут відповіді як вихідну точку, над якою можна було б працювати над рішеннями, а не дурними і бойовими тестами. Я сподіваюся, що вони цінніші за коментарі, які не залишають ніякої інформації. Ви також можете безкоштовно подати заявку. Наскільки я знаю, що далі потрібно зробити, це дотримуватися розбору стандартів. У MS є один на msdn, і мені давно вдавалося виправити. Якби ми всі мали час. "@", будь ласка, майте на увазі крайові випадки, особливо у коментарі Філіпа Атца.
nawfal

@nawfal: Я підійшов і залишив відповідь, як тільки я його вирішив.
Джошуа

15

Ось пара рядків коду, який би розібрав будь-який рядок з'єднання у словнику:

Dictionary<string, string> connStringParts = connString.Split(';')
    .Select(t => t.Split(new char[] { '=' }, 2))
    .ToDictionary(t => t[0].Trim(), t => t[1].Trim(), StringComparer.InvariantCultureIgnoreCase);

І тоді ви можете отримати доступ до будь-якої частини:

string dataSource = connStringParts["Data Source"];

3
Смарт, єдине, що я б змінив, було б включити StringSplitOptions.RemoveEmptyEntriesдо першого розколу, оскільки це спричинить IndexOutOfRangeвиняток, якщо буде кінець;
Скотт Чемберлен

2
Це працює, але будівельники струнних з'єднань більш надійні. Цей код викидає низькі рівні винятків замість більш значущих помилок розбору у випадку недійсних рядків з'єднання.
Сем,

Це допомогло мені проаналізувати рядок з'єднання ADO, щоб я міг створити еквівалент SqlConnectionз SqlConnectionStringBuilder.
Holistic Developer

Деякі застереження тут. Значення рядкових з'єднань можуть бути укладені, наприклад, у лапки.
Сева Алексєєва

6

Використовуйте SqlConnectionStringBuilder На жаль, вам доведеться використовувати специфічні для DB ConnectionStringBuilder, оскільки рядки з'єднання відрізняються.



4

Так, це можна зробити за допомогою класів ConnectionStringBuilder. Ось перелік доступних реалізацій DbConnectionStringBuilder для стандартних постачальників даних:

System.Data.Odbc.OdbcConnectionStringBuilder
System.Data.OleDb.OleDbConnectionStringBuilder
System.Data.OracleClient.OracleConnectionStringBuilder
System.Data.SqlClient.SqlConnectionStringBuilder

ось приклад прикладу рядка з'єднання для розбору та відображення його елементів.

 string conString = @"Data Source=.\sqlexpress;" +
                        "Database=Northwind;Integrated Security=SSPI;" +
                        "Min Pool Size=5;Max Pool Size=15;Connection Reset=True;" +
                        "Connection Lifetime=600;";
    // Parse the SQL Server connection string and display it's properties

    SqlConnectionStringBuilder objSB1 = new SqlConnectionStringBuilder(conString);
    Response.Write("<b>Parsed SQL Connection String Parameters:</b>");
    Response.Write(" <br/>  Database Source = " + objSB1.DataSource);
    Response.Write(" <br/>  Database = " + objSB1.InitialCatalog);
    Response.Write(" <br/>  Use Integrated Security = " + objSB1.IntegratedSecurity);
    Response.Write(" <br/>  Min Pool Size = " + objSB1.MinPoolSize);
    Response.Write(" <br/>  Max Pool Size = " + objSB1.MaxPoolSize);
    Response.Write(" <br/>  Lifetime = " + objSB1.LoadBalanceTimeout);

4

Ви можете використовувати DbConnectionStringBuilder, і вам не потрібен конкретний постачальник:

Наступний код:

var cnstr = "Data Source=data source value;Server=ServerValue";
var builder = new DbConnectionStringBuilder();
builder.ConnectionString = cnstr;
Console.WriteLine("Data Source: {0}", builder["Data Source"]);
Console.WriteLine("Server: {0}", builder["Server"]);

Виходи на консоль:

Data Source: data source value
Server: ServerValue

Редагувати:

Оскільки DbConnectionStringBuilder реалізує IDictionary, ви можете перерахувати параметри рядка з'єднання:

foreach (KeyValuePair<string, object> kv in builder)
{
    Console.WriteLine("{0}: {1}", kv.Key, kv.Value);
}

Це передбачає, що ви вже знаєте, що рядок з'єднання має значення "Джерело даних" тощо, що не завжди відповідає дійсності, як зазначено вище в stackoverflow.com/a/15529085/534109 .
Tieson T.

У моїй відповіді конкретно адресовано те, що запитують: "Я хочу мати можливість зазирнути, наприклад," Джерело даних "'
Jesús López

Я відредагував це, щоб показати всі параметри рядкового з'єднання.
Хесус Лопес

1

Мені не дуже сподобалися всі відповіді тут. Отже ось що я знайшов.

З .NET Core

Ви можете використовувати DbConnectionStringBuilderбезпосередньо:

var builder = new System.Data.Common.DbConnectionStringBuilder();
builder.ConnectionString = settings.ConnectionString;
var server = builder["server"];

0

Тож я виявив, що всі існуючі відповіді були більш-менш помилковими. Я закінчив таке банальне рішення:

class ConnectionStringParser: DbConnectionStringBuilder {
    ConnectionStringParser(string c) { Connection = c; }
    public override bool ShouldSerialize(string keyword) => true;
}

Парсер знаходиться в DbConnectionStringBuilder і в ньому дуже легко потрапити. Єдине нерозумне, що нам потрібно зробити, це встановити ShouldSerialize, щоб завжди повертати true, щоб запобігти втраті компонентів при спробі заокруглення довільних рядків зв'язку.


2
Що було не так у існуючих відповідях? Або що вирішує ваше рішення. Пояснення було б корисним.
nawfal
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.