Gibt es einen Verbindungszeichenfolgenparser in C #?

181

Ich habe eine Verbindungszeichenfolge und möchte beispielsweise "Datenquelle" sehen können. Gibt es einen Parser oder muss ich die Zeichenfolge durchsuchen?

Amir Rezaei
quelle

Antworten:

305

Ja, da ist die System.Data.Common.DbConnectionStringBuilderKlasse.

Die DbConnectionStringBuilder-Klasse stellt die Basisklasse bereit, von der die stark typisierten Builder für Verbindungszeichenfolgen (SqlConnectionStringBuilder, OleDbConnectionStringBuilder usw.) abgeleitet sind. Mit den Buildern für Verbindungszeichenfolgen können Entwickler programmgesteuert syntaktisch korrekte Verbindungszeichenfolgen erstellen und vorhandene Verbindungszeichenfolgen analysieren und neu erstellen.

Die interessierenden Unterklassen sind:

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

Um beispielsweise die Datenquelle aus einer SQL-Server-Verbindungszeichenfolge herauszuschauen, haben Sie folgende Möglichkeiten:

var builder = new SqlConnectionStringBuilder(connectionString);
var dataSource = builder.DataSource;
Ani
quelle
6
Die Basisklasse DbConnectionStringBuilderverfügt über generische Verarbeitungsfunktionen, die ohne Verwendung von Unterklassen verwendet werden können:if (builder.TryGetValue("Password", out var pwd)) { string decrypted = SomehowDecrypt(pwd); builder["Password"] = decrypted; }
Olivier Jacot-Descombes
41

Es gibt herstellerspezifische Verbindungszeichenfolge Bauer von verschiedenen Anbietern wie SqlConnectionStringBuilder, MySqlConnectionStringBuilder, SQLiteConnectionStringBuilderusw. (leider gibt es keine öffentliche Schnittstelle von MS dieses Mal). Andernfalls haben Sie DbProviderFactory.CreateConnectionStringBuilder , mit dem Sie es auf alternative Weise anbieterunabhängig schreiben können. Sie müssten den Anbieter in der Konfigurationsdatei angeben und die richtige Version der DLL zur Verfügung haben. Zum Beispiel

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

Ich hatte einmal eine manuelle Analyse für mich geschrieben, die mir keine Probleme bereitete. Es wäre trivial, dies zu erweitern, um Informationen zu anderen Parametern zu erhalten (derzeit nur für einfache Dinge wie Datenbankname, Datenquelle, Benutzername und Passwort). So oder so:

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

Dafür benötigen Sie nichts Besonderes in der Konfigurationsdatei oder überhaupt eine DLL. ContainsDie WhereKlausel in ist nur dann wichtig, wenn Sie schlecht formatierte Verbindungszeichenfolgen umgehen müssen, z. B. server = localhost;pp;where ppfügt nichts hinzu. Um sich wie normale Builder zu verhalten (die in diesen Fällen explodieren würden), ändern Sie das Wherein

.Where(kvp => !string.IsNullOrWhitespace(kvp))
nawfal
quelle
@Icarus nicht wirklich, da der Schlüsselvergleich des Wörterbuchs ist StringComparer.InvariantCultureIgnoreCase. Siehe die ToDictionaryÜberladung
Nawfal
1
Ja, Sie haben Recht, ich habe gerade einen kurzen Test geschrieben. Löscht meinen ursprünglichen Kommentar, da er falsch ist.
Ikarus
3
Ihre GetValue () -Methode funktioniert nicht in Fällen, in denen der Benutzer beispielsweise ein ';'oder ein '='Kennwort in seinem Kennwort hat. Ich hatte eine ähnliche Implementierung geschrieben und festgestellt, dass es nicht auf die harte Tour funktioniert. Meine Güte, das Parsen von Verbindungszeichenfolgen ist tatsächlich viel schwieriger als ich dachte!
Philip Atz
@ Joshua Ich hoffe, Sie sprechen über den manuellen Parsing-Teil. Bitte nehmen Sie die Antworten hier als Ausgangspunkt, an dem gearbeitet werden könnte, anstatt narrensichere und kampferprobte Lösungen. Ich hoffe, sie sind wertvoller als Kommentare, die keine Informationen hinterlassen. Sie können auch abstimmen. Soweit ich weiß, müssen die Parsing-Standards weiter eingehalten werden. MS hat eine auf msdn und ich habe lange daran gedacht, sie zu verbessern. Wenn wir nur alle Zeit hätten. '@' alle bitte bedenken Sie die Randfälle, besonders einen in Philip Atz 'Kommentar.
Nawfal
@nawfal: Ich kam vorbei und hinterließ eine Antwort, sobald ich es gelöst hatte.
Joshua
15

Hier sind ein paar Codezeilen, die jede Verbindungszeichenfolge in ein Wörterbuch analysieren:

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

Und dann können Sie auf jedes Teil zugreifen:

string dataSource = connStringParts["Data Source"];
codeprose-sam
quelle
3
Smart, das einzige, was ich ändern würde, wäre die Aufnahme StringSplitOptions.RemoveEmptyEntriesin die erste Teilung, da dies eine IndexOutOfRangeAusnahme verursacht, wenn es ein Trailing gibt;
Scott Chamberlain
2
Dies funktioniert, aber die Builder für Verbindungszeichenfolgen sind robuster. Code wie dieser löst bei ungültigen Verbindungszeichenfolgen Ausnahmen auf niedriger Ebene aus, anstatt aussagekräftigere Analysefehler.
Sam
Dies hat mir geholfen, eine ADO-Verbindungszeichenfolge zu analysieren, damit ich eine Entsprechung SqlConnectionmit der erstellen kann SqlConnectionStringBuilder.
Ganzheitlicher Entwickler
Einige Einschränkungen hier. Verbindungszeichenfolgenwerte können beispielsweise in Anführungszeichen gesetzt werden.
Seva Alekseyev
6

Verwenden des SqlConnectionStringBuilder Leider müssen Sie einen DB-spezifischen ConnectionStringBuilder verwenden, da sich die Verbindungszeichenfolgen unterscheiden.

Erno
quelle
4

Ja, Sie können dies mit ConnectionStringBuilder-Klassen tun. Hier ist die Liste der verfügbaren DbConnectionStringBuilder-Implementierungen für Standarddatenanbieter:

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

Hier finden Sie ein Beispiel für eine Analyse-Verbindungszeichenfolge und deren Elemente.

 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);
Jayesh Sorathia
quelle
4

Sie können DbConnectionStringBuilder verwenden und benötigen keinen bestimmten Anbieter:

Der folgende Code:

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

Ausgaben an die Konsole:

Data Source: data source value
Server: ServerValue

BEARBEITEN:

Da DbConnectionStringBuilder IDictionary implementiert, können Sie die Verbindungszeichenfolgenparameter auflisten:

foreach (KeyValuePair<string, object> kv in builder)
{
    Console.WriteLine("{0}: {1}", kv.Key, kv.Value);
}
Jesús López
quelle
Dies setzt voraus, dass Sie bereits wissen, dass die Verbindungszeichenfolge den Wert "Datenquelle" usw. hat, was nicht immer der Fall ist, wie oben unter stackoverflow.com/a/15529085/534109 angegeben .
Tieson T.
Meine Antwort fügt genau das hinzu, was die OP fragt: "Ich möchte in der Lage sein, zum Beispiel" Datenquelle "herauszuschauen"
Jesús López
Ich habe es bearbeitet, um alle Verbindungszeichenfolgenparameter anzuzeigen.
Jesús López
1

Alle Antworten hier haben mir nicht wirklich gefallen. Also hier ist was ich gefunden habe.

Mit .NET Core

Sie können DbConnectionStringBuilderdirekt verwenden:

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

Also fand ich, dass alle vorhandenen Antworten mehr oder weniger falsch waren. Am Ende hatte ich die folgende triviale Lösung:

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

Der Parser befindet sich in DbConnectionStringBuilder und ist ziemlich einfach zu erreichen. Das einzig Dumme, was wir tun müssen, ist, ShouldSerialize so einzustellen, dass immer true zurückgegeben wird, um zu verhindern, dass Komponenten verloren gehen, wenn versucht wird, beliebige Verbindungszeichenfolgen zu umgehen.

Joshua
quelle
2
Was war falsch an vorhandenen Antworten? Oder was behebt Ihre Lösung? Erklärung wäre hilfreich.
Nawfal