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?
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:
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;
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))
StringComparer.InvariantCultureIgnoreCase. Zobacz ToDictionaryprzeciążenie
';'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!
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"];
StringSplitOptions.RemoveEmptyEntriesw pierwszym podziale, ponieważ spowoduje to IndexOutOfRangewyjątek, jeśli dojdzie do końca;
SqlConnectionz SqlConnectionStringBuilder.
Użyj SqlConnectionStringBuilder Niestety będziesz musiał użyć specyficznego dla DB ConnectionStringBuilder, ponieważ parametry połączenia różnią się.
Chcesz użyć DbProviderFactory.CreateConnectionStringBuilder (), który udostępnia konstruktor / parser parametrów połączenia specyficzny dla Twojego konektora, ale nie wymaga użycia żadnych klas specyficznych dla konektora.
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);
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);
}
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ń.
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; }