我开发了一个c#应用程序,它连接到许多类型的数据库服务器,如Sql,Oracle,Mysql等。连接是使用 ODBC 建立的。
我需要找到已建立连接的服务器类型(DBMS类型),因为用户只输入 DSN 名称。
经过几个小时的冲浪,我发现我的要求只能通过这个函数SQLGetInfo。但我不知道如何在C#中处理这个问题。
甚至已经检查了这个Link1。
从上面的链接中,我发现ODBC API只提供DataBase name和DataSource Name。但是我需要找到Datatbase类型,比如它是SQL连接还是Oracle连接还是mySqlConnection。
是否可以从DSN名称获取DBMS类型??
注意:我不想从注册表设置中读取它。我试过这个并且它有效,但我有权限问题..!
答案 0 :(得分:4)
简答:不要。请尽量找到托管等价物。没有记录的方法来获得此处理。
长答案: SQLGetInfo函数的InfoType参数有47个可能的值。 Ref。您可以按如下方式检索带引号的标识符的正则表达式模式:
DataTable dt = connection.GetSchema(DbMetaDataCollectionNames.DataSourceInformation);
string quotedIdentifierPattern = (string)dt.Rows[0][DbMetaDataColumnNames.QuotedIdentifierPattern];
这将允许您识别但不生成带引号的标识符。假设引号字符确实是一个字符是安全的,所以你可以通过简单地做到:
Regex.Unescape(quotedIdentifierPattern)[0];
(.Unescape()是必要的,因为引号字符可能是特殊的 regexen因此逃脱了。)
SQLInfo()的大多数其他用途可以类似地使用.GetSchema()来解决。如果
你绝对肯定必须使用SQLGetInfo()来做某事,我建议
使用私有方法.GetInfoInt16Unhandled()
,.GetInfoInt32Unhandled()
通过反思..GetInfoStringUnhandled()
和OdbcConnection
。这是
但是,如果没有警告就会破裂。
您可以通过私有.ConnectionHandle获取内部句柄 会员,但这同样受到破坏而且不太方便 (因为你必须编写所有非托管互操作代码)。
使用ILSpy或Reflector获取更多信息 有关实施的详细信息。逆向工程内部可以很多 案例指向完全托管的解决方案。 Ref
OR 构建此 MSDN示例代码,以通过不同的命令检测版本,例如
MySQL:“SELECT version()”;
Oracle:“SELECT @@ version,@@ version_comment FROM dual”;
SQLServer:“SELECT @@ version”;
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-Catch's):
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 :(得分: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));
}
我找到的最好的文档,解释了使用SQLGetInfo的47种可能性https://mariadb.com/kb/en/sql-99/sqlgetinfo/
尽管如此,OdbcConnection刚刚集成了一个带有19种可能性的枚举。以下是System.Data.Odbc.ODBC32的SQL_INFO的反汇编枚举:
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
}
如您所见,您只需使用所需信息的(ushort)转换整数调用GetInfoStringUnhandled方法即可。这是一个例子:
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
}
答案 2 :(得分:1)
您是否尝试解析OdbcConnection的.Driver属性? 它将显示连接使用的数据库包装驱动程序。 您也可以在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;
}