Czy w C # jest jakiś parser parametrów połączenia?


181

Mam ciąg połączenia i chcę mieć możliwość podglądania na przykład „źródła danych”. Czy istnieje parser lub czy muszę przeszukiwać ciąg?

Odpowiedzi:


305

Tak, jest System.Data.Common.DbConnectionStringBuilderklasa.

Klasa DbConnectionStringBuilder zapewnia klasę bazową, z której wywodzą się silnie typowane konstruktory parametrów połączenia (SqlConnectionStringBuilder, OleDbConnectionStringBuilder itd.). Konstruktorzy parametrów połączenia pozwalają programistom programowo tworzyć poprawne składniowo parametry połączenia oraz analizować i przebudowywać istniejące parametry połączenia.

Podklasy zainteresowań to:

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

Na przykład, aby „zerkać na źródło danych” z ciągu połączenia z serwerem SQL, możesz:

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

6
Klasa podstawowa DbConnectionStringBuilderma ogólne funkcje przetwarzania, których można używać bez korzystania z podklas:if (builder.TryGetValue("Password", out var pwd)) { string decrypted = SomehowDecrypt(pwd); builder["Password"] = decrypted; }
— Olivier Jacot-Descombes

41

Są to specyficzne połączenie sprzedawca strunowe budowniczych z różnych dostawców, takich jak SqlConnectionStringBuilder, MySqlConnectionStringBuilder, SQLiteConnectionStringBuilderitp (niestety nie ma public interface z SM to czasu). W przeciwnym razie masz DbProviderFactory.CreateConnectionStringBuilder, który da ci alternatywny sposób na napisanie go w sposób niezależny od dostawcy. Musisz podać dostawcę w pliku konfiguracyjnym i mieć dostępną odpowiednią wersję biblioteki DLL. Na przykład

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"];

Kiedyś napisałem dla siebie ręczne parsowanie, które nie sprawiło mi żadnych problemów. Rozbudowanie tego byłoby trywialne, aby podawać informacje o innych parametrach (obecnie jest to tylko proste rzeczy, takie jak nazwa bazy danych, źródło danych, nazwa użytkownika i hasło). Tak lub mniej:

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;
}

W tym celu nie potrzebujesz niczego specjalnego w pliku konfiguracyjnym ani żadnej dll. Containsw Whereklauzuli jest ważne tylko wtedy, gdy trzeba ominąć źle sformatowane parametry połączenia, np. server = localhost;pp;gdzie ppnic nie dodaje. Aby zachowywać się jak normalni budowniczowie (którzy w takich przypadkach eksplodowaliby), zmień Wherena

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

@Icarus nie jest tak naprawdę, ponieważ głównym porównywaczem słownika jest StringComparer.InvariantCultureIgnoreCase. Zobacz ToDictionaryprzeciążenie
— nawfal

1
Tak, masz rację, właśnie napisałem szybki test. Usunie mój oryginalny komentarz, ponieważ jest nieprawidłowy.
— Icarus

3
Metoda Twój GetValue () nie będzie działać w przypadku, gdy na przykład użytkownik ma ';'lub '='ich hasłem. Napisałem podobną implementację i dowiedziałem się, że to nie działa na poważnie. Rany, parsowanie połączeń jest w rzeczywistości o wiele trudniejsze niż myślałem!
— Philip Atz,

@Joshua Mam nadzieję, że mówisz o części dotyczącej ręcznej analizy. Proszę wziąć tutaj odpowiedzi jako punkt wyjścia, nad którym można byłoby pracować zamiast niezawodnych i przetestowanych w walce rozwiązań. Mam nadzieję, że są one cenniejsze niż komentarze, które nie pozostawiają żadnych informacji. Możesz również głosować za darmo. O ile mi wiadomo, należy dalej postępować zgodnie z parsującymi standardami. Stwardnienie rozsiane ma jeden na msdn i od dawna myślałem o naprawieniu. Gdybyśmy wszyscy mieli czas. „@” wszyscy powinni pamiętać o przypadkach skrajnych, szczególnie jeden w komentarzu Philipa Atza.
— nawfal

@nawfal: Przyszedłem i zostawiłem odpowiedź, kiedy ją rozwiązałem.
— Joshua,

15

Oto kilka wierszy kodu, które parsują dowolny ciąg połączenia w słowniku:

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

Następnie możesz uzyskać dostęp do dowolnej części:

string dataSource = connStringParts["Data Source"];

3
Sprytne, jedyne, co chciałbym zmienić, to uwzględnić StringSplitOptions.RemoveEmptyEntriesw pierwszym podziale, ponieważ spowoduje to IndexOutOfRangewyjątek, jeśli dojdzie do końca;
— Scott Chamberlain

2
Działa to, ale konstruktory parametrów połączenia są bardziej niezawodne. Taki kod generuje wyjątki niskiego poziomu zamiast bardziej znaczących błędów analizy w przypadku niepoprawnych ciągów połączenia.
— Sam

Pomogło mi to przeanalizować parametry połączenia ADO, dzięki czemu mogłem zbudować odpowiednik SqlConnectionz SqlConnectionStringBuilder.
— Holistic Developer

Kilka ostrzeżeń tutaj. Wartości ciągu połączenia mogą być na przykład ujęte w cudzysłowy.
— Seva Alekseyev



4

Tak, możesz to zrobić za pomocą klas ConnectionStringBuilder. Oto lista dostępnych implementacji DbConnectionStringBuilder dla standardowych dostawców danych:

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

oto przykładowy przykład parsowania ciągu połączenia i wyświetl jego elementy.

 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

Możesz użyć DbConnectionStringBuilder i nie potrzebujesz żadnego konkretnego dostawcy:

Poniższy kod:

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"]);

Wyjścia na konsolę:

Data Source: data source value
Server: ServerValue

EDYTOWAĆ:

Ponieważ DbConnectionStringBuilder implementuje IDictionary, możesz wyliczyć parametry ciągu połączenia:

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

Zakłada się, że już wiesz, że ciąg połączenia ma wartość „Źródło danych” itp., Co nie zawsze jest prawdziwe, jak zauważono w stackoverflow.com/a/15529085/534109 powyżej.
— Tieson T.,

Moja odpowiedź jest zgodna z tym, o co pytają operatorzy: „Chcę móc na przykład zajrzeć do„ Źródła danych ”
— Jesús López,

Zredagowałem go, aby pokazać wszystkie parametry ciągu połączenia.
— Jesús López

1

Nie podobały mi się tutaj wszystkie odpowiedzi. Oto co znalazłem.

Z .NET Core

Możesz użyć DbConnectionStringBuilderbezpośrednio:

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

0

Stwierdziłem więc, że wszystkie istniejące odpowiedzi były mniej więcej błędne. Skończyło się na następującym trywialnym rozwiązaniu:

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

Analizator składni znajduje się w DbConnectionStringBuilder i jest bardzo łatwy do uzyskania. Jedyną głupią rzeczą, którą musimy zrobić, jest ustawienie opcji ShouldSerialize, aby zawsze zwracała wartość true, aby zapobiec utracie komponentów podczas próby obejścia dowolnych ciągów połączeń.


2
Co było nie tak z istniejącymi odpowiedziami? Lub co naprawia twoje rozwiązanie. Wyjaśnienie byłoby pomocne.
— nawfal,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.