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.