2015-05-20 3 views
11

Я разработал приложение C#, которое подключается ко многим типам серверов баз данных, таких как Sql, Oracle, Mysql и т. Д. Соединение было установлено с использованием ODBC.SQLGetInfo - Как использовать эту функцию

Мне нужно найти тип сервера (тип СУБД), для которого установлено соединение, так как пользователь вводит имя DSN.

После нескольких часов серфинга я обнаружил, что мое требование возможно только через эту функцию SQLGetInfo. Но я не знаю, как с этим справиться в C#.

даже проверили это Link1.

по ссылке выше, я обнаружил, что ODBC API дает только DataBase name и DataSource Name .Но мне нужно найти тип Datatbase, как будь то подключение к SQL или подключение к Oracle или mySqlConnection.

Возможно ли получить тип СУБД с именем DSN ..?

Примечание: я не хочу читать его из настройки реестра. Я пробовал это, и он работает, но у меня возникают проблемы с разрешением.!

+0

В чем дело. Я не имею в виду это снисходительно. При всем уважении, почему вы не используете шаблон IDBConnection ** Factory **? Таким образом, вам не нужно знать производителя БД и нужно только написать один DAL? Если вы не пишете ORM, это огромные накладные расходы, предназначенные для разных БД, и для получения большого выигрыша - особенно если вы не делаете ничего слишком специфического. EF, Mindscape LightSpeed, Hibernate и т. Д. Предлагают лучшие решения - даже если эти продукты требуют, чтобы конечные пользователи вручную указывали тип базы данных. –

ответ

4

Это будет абсолютно сделать трюк. Вот моя реализация, чтобы подключить функцию OdbcConnection GetInfoStringUnhandled. Бог мы любим отражение, Я знаю, что я легенда;)

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

Лучшая документация Я нашел, объясняя 47 возможности использования SQLGetInfo является здесь https://mariadb.com/kb/en/sql-99/sqlgetinfo/

Тем не менее, OdbcConnection только интегрировали перечисление с 19 возможностями. Ниже приводится разобранное перечисление из SQL_INFO в 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 
} 

Как вы видите, вы можете просто вызовите метод GetInfoStringUnhandled с (USHORT) отлито целое числом информации вам нужно. Это пример:

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

Действительно спасибо человеку .. !! – Arshad

1

Вы пытались проанализировать свойство .Driver OdbcConnection? Он покажет вам используемый драйвер оболочки базы данных для подключения. Вы можете найти эти отображения также в реестре в разделе HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ ODBC

Иногда это просто .dll имя (например, для драйвера Microsoft Excel), но это может дать вам подсказку.

Вы можете также выполнять определенные команды базы данных, чтобы получить версию базы данных с сервера, как так:

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

Из драйвера мы можем получить только имя Dll, а не тип СУБД или имя драйвера @Fabian Stern .... И тоже в этом реестре мы не получим тип СУБД. Я уже проверил это. И мне нужно найти тип сервера, такой как SQL, MySQL, Oracle и т. д. – Arshad

+0

На самом деле такого типа нет. То, что вы можете иметь, это драйвер .dll, и это также тип используемой СУБД. Это единственная возможность, которую я знаю. Другим подходом было бы выполнение начальных строк для каждой системы баз данных «Oracle», «MSSQL», «mySQL», которая возвращает их соответствующие версии. Я сейчас поработаю над этим и дам вам ответный ответ –

+0

Я не мог получить этот Другой подход состоял бы в том, чтобы выполнить начальные строки для каждой системы баз данных «Oracle», «MSSQL», «mySQL», которые возвращают свои соответствующие версии ... !! Будем ждать ваших отзывов в любом случае – Arshad

4

Короткий ответ: не делать. Попробуйте очень трудно найти управляемые эквиваленты. Нет документального способа получения этого дескриптора.

Длинный ответ: Параметр InfoType функции SQLGetInfo имеет 47 возможных значений. Ref. Вы можете получить регулярное выражение для цитируемых идентификаторов следующим образом:

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

Это позволит вам распознать, но не производить идентификаторы в кавычках. Это с уверенностью предположить, котировка характер действительно один персонаж, хотя, так что вы можете получить его просто делать:

Regex.Unescape(quotedIdentifierPattern)[0]; 

(.Unescape() является необходимым, так как кавычка может быть особенным regexen и, следовательно, escaped.)

Большинство других применений для SQLInfo() можно аналогичным образом решить с помощью .GetSchema(). Если вы абсолютно положительно должны использовать SQLGetInfo() что-то я рекомендую , используя частные методы .GetInfoInt16Unhandled(), .GetInfoInt32Unhandled() и ..GetInfoStringUnhandled() на OdbcConnection посредством отражения. Это , подлежащие разрыву без предупреждения.

Вы можете получить внутреннюю ручку через частный .ConnectionHandle члена, но это в равной степени подвержены нарушения и гораздо менее удобно (потому что вы должны написать все неуправляемый код Interop тоже).

Используйте ILSpy или Reflector, чтобы получить более подробное описание . Обратное проектирование внутренних дел во многих случаях указывает на полностью управляемое решение. Ref.


ИЛИ сборки на этом MSDN образец кода для обнаружения версии с помощью различных команд, например

MySQL:         "ВЫБОР версии()";
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(); 
     } 
     } 
    } 
} 

ИЛИ вы могли бы сделать что-то вроде другие предполагают, с немного больше элегантности.

Примечание: это копирование/вставка задания на ответ @FabianStern - кредит автору. Я только что сделал это менее процедурным и более ортодоксальным, как я не мог стоять каскадные Try-Поймай в):

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

Вы смешали строки запросов Oracle и mySQL, скопировали и вставляли с большей элегантностью;) –

Смежные вопросы