2015-05-20 19 views
11

Ho sviluppato un'applicazione C# che si collega a molti tipi di server di database come Sql, Oracle, Mysql ecc. La connessione è stata stabilita utilizzando ODBC.SQLGetInfo - Come utilizzare questa funzione

Devo trovare il tipo di server (tipo DBMS) per il quale è stata stabilita la connessione, poiché l'utente immette solo il nome DSN.

Dopo diverse ore di navigazione, ho trovato che il mio requisito è possibile solo attraverso questa funzione SQLGetInfo. Ma non so come affrontare questo in C#.

anche aver controllato questo Link1.

dal link qui sopra, ho scoperto che ODBC API dà solo DataBase name e DataSource Name .Ma ho bisogno di trovare il tipo Datatbase come se si tratta di una connessione SQL o una connessione Oracle o MySqlConnection.

È possibile ottenere il tipo di DBMS dal nome DSN ...?

Nota: non voglio leggerlo da un'impostazione di registro. Ho provato questo e funziona, ma sto avendo problemi di autorizzazione ..!

+0

Qual è il punto della domanda .. Non intendo questo condiscendente. Con tutto il dovuto rispetto perché non usi il pattern IDBConnection ** Factory **? In questo modo non hai bisogno di conoscere il produttore del DB e devi solo scrivere un DAL? A meno che non si stia scrivendo un ORM, si tratta di un enorme overhead che si rivolge a diversi DB e non ha molto guadagno, specialmente quando non si sta facendo qualcosa di troppo specifico. EF, Mindscape LightSpeed, Hibernate, ecc. Offrono tutte soluzioni migliori - MA anche questi prodotti richiedono agli utenti finali di specificare manualmente il tipo di database. –

risposta

4

Questo sarà assolutamente fare il trucco. Ecco la mia implementazione per collegare la funzione GetInfoStringUnhandled di OdbcConnection. Dio amiamo riflessione, so di leggenda;)

public enum SQL_INFO 
{ 
    DATA_SOURCE_NAME, 
    DRIVER_NAME, 
    DRIVER_VER, 
    ODBC_VER, 
    SERVER_NAME, 
    SEARCH_PATTERN_ESCAPE, 
    DBMS_NAME, 
    DBMS_VER, 
    IDENTIFIER_CASE, 
    IDENTIFIER_QUOTE_CHAR, 
    CATALOG_NAME_SEPARATOR, 
    DRIVER_ODBC_VER, 
    GROUP_BY, 
    KEYWORDS, 
    ORDER_BY_COLUMNS_IN_SELECT, 
    QUOTED_IDENTIFIER_CASE, 
    SQL_OJ_CAPABILITIES_30, 
    SQL_SQL92_RELATIONAL_JOIN_OPERATORS, 
    SQL_OJ_CAPABILITIES_20 

} 

public static string GetInfoStringUnhandled(OdbcConnection ocn, SQL_INFO info) 
{ 
    MethodInfo GetInfoStringUnhandled = ocn.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).First(c => c.Name == "GetInfoStringUnhandled"); 
    ParameterInfo SQL_INFO = 
     GetInfoStringUnhandled.GetParameters() 
      .First(c => (c.ParameterType.ToString() == "System.Data.Odbc.ODBC32+SQL_INFO")); 
    Array EnumValues = SQL_INFO.ParameterType.GetEnumValues(); 
    foreach (var enumval in EnumValues) { 
     if (enumval.ToString() == info.ToString()) { 
      return Convert.ToString(GetInfoStringUnhandled.Invoke(ocn, new object[] { enumval })); 
     } 
    } 
    return string.Empty; 
} 

private static void Main(string[] args) 
{ 
    OdbcConnection ocn = new OdbcConnection("DSN=GENESIS"); 
    ocn.Open(); 
    Console.WriteLine(GetInfoStringUnhandled(ocn, SQL_INFO.DBMS_VER) + " " + 
         GetInfoStringUnhandled(ocn, SQL_INFO.DBMS_NAME)); 
} 

La documentazione migliore che ho trovato, spiegando le 47 possibilità di utilizzare SQLGetInfo è qui https://mariadb.com/kb/en/sql-99/sqlgetinfo/

Tuttavia, OdbcConnection ha appena integrato un enum con 19 possibilità. Quello che segue è un enum disassemblato di SQL_INFO di System.Data.Odbc.ODBC32:

public enum SQL_INFO : ushort 
{ 
    DATA_SOURCE_NAME = (ushort)2, 
    DRIVER_NAME = (ushort)6, 
    DRIVER_VER = (ushort)7, 
    ODBC_VER = (ushort)10, 
    SERVER_NAME = (ushort)13, 
    SEARCH_PATTERN_ESCAPE = (ushort)14, 
    DBMS_NAME = (ushort)17, 
    DBMS_VER = (ushort)18, 
    IDENTIFIER_CASE = (ushort)28, 
    IDENTIFIER_QUOTE_CHAR = (ushort)29, 
    CATALOG_NAME_SEPARATOR = (ushort)41, 
    DRIVER_ODBC_VER = (ushort)77, 
    GROUP_BY = (ushort)88, 
    KEYWORDS = (ushort)89, 
    ORDER_BY_COLUMNS_IN_SELECT = (ushort)90, 
    QUOTED_IDENTIFIER_CASE = (ushort)93, 
    SQL_OJ_CAPABILITIES_30 = (ushort)115, 
    SQL_SQL92_RELATIONAL_JOIN_OPERATORS = (ushort)161, 
    SQL_OJ_CAPABILITIES_20 = (ushort)65003 
} 

Come si vede, si può semplicemente Richiamare il metodo GetInfoStringUnhandled con un (USHORT) colato intero della informazioni che ti servono. Questo è un esempio:

public static string GetInfoStringUnhandled(OdbcConnection ocn, ushort info) 
{ 
    MethodInfo GetInfoStringUnhandled = ocn.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).First(c => c.Name == "GetInfoStringUnhandled"); 
    return Convert.ToString(GetInfoStringUnhandled.Invoke(ocn, new object[] { (ushort)info }));    
} 

public static void Main(string[] args) 
{ 
    OdbcConnection ocn = new OdbcConnection("DSN=GENESIS"); 
    ocn.Open(); 
    Console.WriteLine(GetInfoStringUnhandled(ocn, (ushort)10003)); //SQL_CATALOG_NAME returns Y 
} 
+1

Davvero grazie amico .. !! – Arshad

1

Hai provato a analizzare la proprietà .Driver di OdbcConnection? Mostrerà il driver del wrapper del database usato per la connessione. Puoi trovare questi mapping anche nel registro HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ ODBC

A volte è solo un nome .dll (ad esempio per Microsoft Excel Driver) ma potrebbe darti un suggerimento.

È inoltre possibile eseguire comandi specifici del database per ottenere la versione base di dati dal server in questo modo:

static void Main(string[] args) 
    { 
     var cn = new OdbcConnection("DSN=mysql1"); 
     Console.WriteLine("DBType: {0}", GetDbType(cn)); 
     Console.Read(); 
    } 

    public enum DbType 
    { 
     UNSUPPORTED = 0, 
     MYSQL = 1, 
     ORACLE = 2, 
     MSSQL = 3, 
     POSTGRESQL = 4, 
     JET = 5 
    } 

    public static DbType GetDbType(OdbcConnection cn) 
    { 
     DbType t = DbType.UNSUPPORTED; 
      try 
      { 
       if (cn.State != ConnectionState.Open) cn.Open(); 

       if (cn.Driver == "odbcjt32.dll") 
       { 
        return DbType.JET; 
       } 

       var cmd = cn.CreateCommand(); 
       string outstring = ""; 
       cmd.CommandText = "SELECT * FROM v$version"; 
       try 
       { 
        var reader = cmd.ExecuteReader(); 
        if (reader.HasRows) 
        { 
         reader.Read(); 
         outstring = String.Format("{0}", reader.GetString(0)); 

        } 
       } 
       catch (Exception) 
       { 
        cmd = cn.CreateCommand(); 
        cmd.CommandText = "SELECT @@version, @@version_comment FROM dual"; 
        try 
        { 
         var reader = cmd.ExecuteReader(); 
         if (reader.HasRows) 
         { 
          reader.Read(); 
          outstring = String.Format("{0} {1}", reader.GetString(0), reader.GetString(1)); 

         } 
        } 
        catch (Exception) 
        { 
         cmd = cn.CreateCommand(); 
         cmd.CommandText = "SELECT @@version"; 
         try 
         { 
          var reader = cmd.ExecuteReader(); 
          if (reader.HasRows) 
          { 
           reader.Read(); 
           outstring = String.Format("{0}", reader.GetString(0)); 

          } 
         } 
         catch (Exception) 
         { 
          cmd = cn.CreateCommand(); 
          cmd.CommandText = "SELECT version()"; 
          try 
          { 
           var reader = cmd.ExecuteReader(); 
           if (reader.HasRows) 
           { 
            reader.Read(); 
            outstring = String.Format("{0}", reader.GetString(0)); 

           } 
          } 
          catch (Exception) 
          { 
          } 
         } 
        } 
       } 

       outstring = outstring.ToUpper(); 

       if (outstring.Contains("MYSQL")) 
       { 
        t = DbType.MYSQL; 
       } 
       else if (outstring.Contains("ORACLE")) 
       { 
        t = DbType.ORACLE; 
       } 
       else if (outstring.Contains("SQL SERVER")) 
       { 
        t = DbType.MSSQL; 
       } 
       else if (outstring.Contains("POSTGRESQL")) 
       { 
        t = DbType.POSTGRESQL; 
       } 


      } 
      catch (Exception E) 
      { 

      } 
     return t; 

    } 
+0

Dal driver possiamo solo ottenere il nome Dll e non il tipo di DBMS o il nome del driver @Fabian Stern .... E anche in quel registro non otterremo il tipo di DBMS. Ho già controllato con quello.! E ho bisogno di trova il tipo di server come SQL, MySQL, Oracle ecc. – Arshad

+0

Non esiste alcun tipo in realtà. Quello che puoi avere è il driver .dll e questo è anche il tipo di DBMS usato. È l'unica possibilità che conosco. Un altro approccio sarebbe quello di eseguire stringhe iniziali per ciascun sistema di database "Oracle", "MSSQL", "mySQL" che restituiscono le rispettive versioni. Lavorerò su questo ora e ti fornirò un feedback –

+0

Non riuscivo a ottenere questo Un altro approccio sarebbe quello di eseguire stringhe iniziali per ogni sistema di database "Oracle", "MSSQL", "mySQL" che restituiscono le rispettive versioni ... !! Attenderei comunque il tuo feedback – Arshad

4

Risposta breve: non lo fanno. Cerca molto difficile trovare equivalenti gestiti invece. Non esiste un modo documentato per ottenere questo handle.

Risposta lunga: Il parametro InfoType della funzione SQLGetInfo ha 47 valori possibili. Ref. È possibile recuperare un modello di espressione regolare per identificatori tra virgolette come segue:

DataTable dt = connection.GetSchema(DbMetaDataCollectionNames.DataSourceInformation); 
string quotedIdentifierPattern = (string)dt.Rows[0][DbMetaDataColumnNames.QuotedIdentifierPattern]; 

Questo vi permetterà di riconoscere, ma non produce identificatori tra virgolette. E 'lecito ritenere il carattere di citazione è davvero un personaggio, però, così si può ottenere semplicemente facendo:

Regex.Unescape(quotedIdentifierPattern)[0]; 

(The .Unescape() è necessaria in quanto il carattere preventivo potrebbe essere speciale per regexen e, quindi, escape.)

La maggior parte degli altri usi di SQLInfo() può essere risolta in modo simile con .GetSchema(). Se si assolutamente, positivamente necessario utilizzare SQLGetInfo() per qualcosa, vi consiglio utilizzando i metodi privati ​​.GetInfoInt16Unhandled(), .GetInfoInt32Unhandled() e ..GetInfoStringUnhandled() su OdbcConnection attraverso la riflessione. Questo è soggetto a rottura senza preavviso, però.

è possibile ottenere la maniglia interna attraverso il privato .ConnectionHandle membro, ma questo è ugualmente soggetto alla rottura e di gran lunga meno conveniente (perché devi scrivere tutto il codice di interoperabilità non gestito troppo).

Utilizzare ILSpy o Reflector per ottenere ulteriori dettagli sull'implementazione. Il reverse engineering degli interni può in molti casi indirizzarvi verso una soluzione completamente gestita. Ref.


O build su questo MSDN codice di esempio per rilevare la versione tramite diversi comandi, ad esempio

MySQL:         "versione SELECT()";
Oracle:         "SELECT @@ version, @@ version_comment FROM dual";
SQLServer:   "SELECT @@ version";

MSDN Sample Code:

using System; 
using System.Data; 

namespace IDbConnectionSample { 
    class Program { 
     static void Main(string[] args) { 
     IDbConnection connection; 

     // First use a SqlClient connection 
     connection = new System.Data.SqlClient.SqlConnection(@"Server=(localdb)\V11.0"); 
     Console.WriteLine("SqlClient\r\n{0}", GetServerVersion(connection)); 
     connection = new System.Data.SqlClient.SqlConnection(@"Server=(local);Integrated Security=true"); 
     Console.WriteLine("SqlClient\r\n{0}", GetServerVersion(connection)); 

     // Call the same method using ODBC 
     // NOTE: LocalDB requires the SQL Server 2012 Native Client ODBC driver 
     connection = new System.Data.Odbc.OdbcConnection(@"Driver={SQL Server Native Client 11.0};Server=(localdb)\v11.0"); 
     Console.WriteLine("ODBC\r\n{0}", GetServerVersion(connection)); 
     connection = new System.Data.Odbc.OdbcConnection(@"Driver={SQL Server Native Client 11.0};Server=(local);Trusted_Connection=yes"); 
     Console.WriteLine("ODBC\r\n{0}", GetServerVersion(connection)); 

     // Call the same method using OLE DB 
     connection = new System.Data.OleDb.OleDbConnection(@"Provider=SQLNCLI11;Server=(localdb)\v11.0;Trusted_Connection=yes;"); 
     Console.WriteLine("OLE DB\r\n{0}", GetServerVersion(connection)); 
     connection = new System.Data.OleDb.OleDbConnection(@"Provider=SQLNCLI11;Server=(local);Trusted_Connection=yes;"); 
     Console.WriteLine("OLE DB\r\n{0}", GetServerVersion(connection)); 
     } 

     public static string GetServerVersion(IDbConnection connection) { 
     // Ensure that the connection is opened (otherwise executing the command will fail) 
     ConnectionState originalState = connection.State; 
     if (originalState != ConnectionState.Open) 
      connection.Open(); 
     try { 
      // Create a command to get the server version 
      IDbCommand command = connection.CreateCommand(); 
      command.CommandText = "SELECT @@version"; //<- HERE 
      //try out the different commands by passing the CommandText as a parameter 
      return (string)command.ExecuteScalar(); 
     } 
     finally { 
      // Close the connection if that's how we got it 
      if (originalState == ConnectionState.Closed) 
       connection.Close(); 
     } 
     } 
    } 
} 

O si potrebbe fare qualcosa di simile altri suggeriscono, con un po 'più di eleganza.

Nota: questo è un lavoro di copia/incolla sulla risposta di @FabianStern - credito all'autore. Ho appena fatto meno procedurale e più ortodossa come non ho potuto resistere alla cascata del try-catch):

protected static DBType GetDBType(string odbcConnStr) 
{ 
var dbType = DBType.UNSUPPORTED; 
try 
{ 
    using (var cn = new OdbcConnection(odbcConnStr)) 
    { 
    if (cn.State != ConnectionState.Open) cn.Open(); 
    dbType = GetDbType(cn, dbType) 
    if (dbType > 0) return dbType; 

    var sqlVersionQuery = "SELECT version()"; 
    dbType = GetDbType(cn, sqlVersionQuery, DBType.MYSQL) 
    if (dbType > 0) return dbType; 

    sqlVersionQuery = "SELECT @@version, @@version_comment FROM dual"; 
    dbType = GetDbType(cn, sqlVersionQuery, DBType.Oracle) 
    if (dbType > 0) return dbType; 

    sqlVersionQuery = "SELECT @@version"; 
    dbType = GetDbType(cn, sqlVersionQuery, DBType.MSSQL) 
    if (dbType > 0) return dbType; 
    } 
} 
catch(Exception connEx) { } 
return dbType; 
} 

public enum DBType 
{ 
    UNSUPPORTED = 0, 
    MYSQL = 1, 
    ORACLE = 2, 
    MSSQL = 3, 
    JET = 4 
} 

private static DBType GetDBType(OdbcConnection cn, DBType dbType) 
{ 
    try 
    { 
    if (cn.Driver == "odbcjt32.dll") dbType = DBType.JET; 
    } 
    catch(Exception ex) { } 
    return dbType; 
} 

private static DBType GetDbType(OdbcConnection cn, string sqlVersionQuery, DBType dbType) 
{ 
    try 
    { 
    using (var cmd = cn.CreateCommand()) { 
    cmd.CommandText = sqlVersionQuery; 
    try 
    { 
     using (var reader = cmd.ExecuteReader()) 
     { 
      if (reader.HasRows) return dbType; 
     } 
    } 
    catch (Exception ex) { } 
    }} 
    catch (Exception cmdEx) { }   
    } 
return dbType; 
} 
+1

hai confuso le stringhe di query Oracle e mySQL, copia e incolla con più eleganza;) –

Problemi correlati