从 dotnet core 3.1 容器到 Netezza 的 ODBC 连接

发布于 2025-01-12 23:52:42 字数 6474 浏览 3 评论 0原文

需要构建一个在 dotnet core 3.1(客户约束)上运行并连接到 Netezza 后端的微服务。

经过多次尝试/错误后,我成功制作了一个 Dockerfile,允许构建容器并运行代码。

但我遇到了困难:我使用带有所有连接参数的连接字符串,但是当我尝试运行代码时,出现以下异常:

System.Data.Odbc.OdbcException (0x80131937): ERROR [HY000B�H�d5Serv���p�U] [unixODBC]Server and/or port attributes are empty��d �
   at System.Data.Odbc.OdbcConnection.HandleError(OdbcHandle hrHandle, RetCode retcode)
   at System.Data.Odbc.OdbcConnectionHandle..ctor(OdbcConnection connection, OdbcConnectionString constr, OdbcEnvironmentHandle environmentHandle)
   at System.Data.Odbc.OdbcConnectionOpen..ctor(OdbcConnection outerConnection, OdbcConnectionString connectionOptions)
   at System.Data.Odbc.OdbcConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningObject)
   at System.Data.ProviderBase.DbConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionInternal.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.Odbc.OdbcConnection.Open()

我已在 DebugLogging=true code>/etc/odbcinst.ini 并在生成的日志中我发现连接字符串被截断:

...
SQLDriverConnectW:  entering    conn = 0x4317BFE0
    SQLDriverConnectInternal:   entering    conn = 0x4317BFE0
    SQLDriverConnectInternal:       nDriverCompletion=0, connStrIn='(D;'
readDSNConfig:   newDSN = 1, File = /usr/local/etc/odbc.ini, Section = NetezzaSQL
readDSNConfig:   Server = 
readDSNConfig:   Database = 
readDSNConfig:   Schema = 
readDSNConfig:   Port = 5480
readDSNConfig:   ReadOnly = 0
readDSNConfig:   BitOneZero = 0
readDSNConfig:   FastSelect = 0
readDSNConfig:   StripCrLf = 0
readDSNConfig:   LegacySQLTables = 0
readDSNConfig:   NumericAsChar = 0
readDSNConfig:   ShowSystemTables = 0
readDSNConfig:   securityLevel = preferredUnSecured
readDSNConfig:   Security level requested = 0
readDSNConfig:   CA certificate used = 
readDSNConfig:   LoginTimeout = 0
readDSNConfig:   QueryTimeout = 0
readDSNConfig:   DateFormat = 1
readDSNConfig:   loadMaxErrors = 1
readDSNConfig:   loadRoundFraction = 0
readDSNConfig:   User ID = 
readDSNConfig:   Workstation Name = 
readDSNConfig:   Application Name = 
readDSNConfig:   Acct String = 
readDSNConfig:   Program Info = 
readDSNConfig:   IgnoreFloatingPointTruncation = 0
    Conn_connect:   Entering    do_password = 0
Connection Error: Function = 'Conn_connect', Line = 1504, State = 'HY000', number = 33, Msg = 'Server and/or port attributes are empty'
...

我尝试使用 DSN 或更改连接字符串中值的顺序。例如,我以分号开始字符串,并在日志中更改为显示:connStrIn='(;'。如果我将连接字符串留空或设置为无效字符串,dotnet 会抱怨 <代码> System.Data.Odbc.OdbcException(0x80131937):错误[IM002] [unixODBC] [驱动程序管理器]未找到数据源名称,并且未指定默认驱动程序所以,我可以看出“Driver”或“Dsn”部分首先被使用,但似乎驱动程序没有正确传递/接收。 我还在文件 odbc.ini 上创建了一个带有连接参数的 [NetezzaSQL] 部分,但这里失败并显示用户 ID 无效错误(尽管我已经为其提供了“用户名”密钥)。然而,基于文件的 DSN 并不合适,因为出于明显的安全原因,凭据无法存储在容器中。

如果我访问容器并尝试使用连接参数或定义的 DSN 执行 nzodbcsql,它会很好地连接并允许我愉快地运行查询。

我使用的驱动程序版本是Release 7.2.0.0 [Build 40845]。 NPS 版本为:11.02.0001 版本 11.2.1.0 [Build 30]

这是我的 Dockerfile:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-bionic AS base

USER root
RUN apt-get update && apt-get -y -qq install make gcc libc6-i386 zlib1g unixodbc unixodbc-dev


COPY netezza.tar.gz /opt/

RUN tar -zxvf /opt/netezza.tar.gz -C /opt/
RUN /opt/netezza/linux64/unpack -f /usr/local/nz

RUN sed -i 's/DebugLogging=false/DebugLogging=true/g' /etc/odbcinst.ini
COPY .odbc.ini /etc/odbc.ini

RUN echo '/usr/local/nz/lib' >> /etc/ld.so.conf.d/x86_64-linux-gnu.conf
RUN echo '/usr/local/nz/lib64' >> /etc/ld.so.conf.d/x86_64-linux-gnu.conf
RUN ldconfig
RUN ln -s /usr/local/nz/bin64/nzodbcsql /usr/local/bin/nzodbcsql

USER 1001
ENV PATH=$PATH:/usr/local/nz/bin64/:
ENV NZ_ODBC_INI_PATH=/etc/
ENV ODBCINI=/etc/odbc.ini
WORKDIR /app


FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build
WORKDIR /src
COPY ["testprg.csproj", "appsettings.json", "appsettings.Development.json", "Program.cs", "./"]
RUN dotnet restore "testprg.csproj"
#COPY . . 
WORKDIR /src
RUN dotnet build "testprg.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "testprg.csproj" -c Release -o /app

FROM base AS final
workdir /app
COPY --from=publish /app .

ENTRYPOINT ["dotnet", "testprg.dll"]

这是我的测试程序:

using System;
using System.Data.Odbc;

namespace testprg
{
    public class Program
    {
        public static void Main(string[] args)
        {
           String connString = "Driver={NetezzaSQL};Servername=*****;Port=5480;Database=*****;Username=****;Password=*****";
           OdbcConnection conn = new OdbcConnection(connString.Normalize());
           
           OdbcCommand cmd = conn.CreateCommand();
           cmd.CommandText="SELECT COUNT(*) FROM TEST_TABLE;";
           cmd.CommandType=System.Data.CommandType.Text;
           Console.OutputEncoding = System.Text.Encoding.Unicode;
           try {
               Console.WriteLine(conn.ConnectionString);
               conn.Open();
               OdbcDataReader reader = cmd.ExecuteReader();
               while (reader.Read()) {
                   Console.WriteLine(reader["COUNT"].ToString());
               }
           } catch (OdbcException e) {
               Console.WriteLine(e);
           }

        }
    }
}

Need to buid a microservice that runs on dotnet core 3.1 (customer constraint) and connects to a Netezza backend.

I've managed to craft a Dockerfile that allows the container to be built and the code to run, after several trial/error efforts.

But I've hit a wall: I use a connection string with all the connection parameters but, when I try to run my code, I get the following exception:

System.Data.Odbc.OdbcException (0x80131937): ERROR [HY000B�H�d5Serv���p�U] [unixODBC]Server and/or port attributes are empty��d �
   at System.Data.Odbc.OdbcConnection.HandleError(OdbcHandle hrHandle, RetCode retcode)
   at System.Data.Odbc.OdbcConnectionHandle..ctor(OdbcConnection connection, OdbcConnectionString constr, OdbcEnvironmentHandle environmentHandle)
   at System.Data.Odbc.OdbcConnectionOpen..ctor(OdbcConnection outerConnection, OdbcConnectionString connectionOptions)
   at System.Data.Odbc.OdbcConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningObject)
   at System.Data.ProviderBase.DbConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionInternal.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.Odbc.OdbcConnection.Open()

I've enabled DebugLogging=true in /etc/odbcinst.ini and on the generated log I found that the connection string is being truncated:

...
SQLDriverConnectW:  entering    conn = 0x4317BFE0
    SQLDriverConnectInternal:   entering    conn = 0x4317BFE0
    SQLDriverConnectInternal:       nDriverCompletion=0, connStrIn='(D;'
readDSNConfig:   newDSN = 1, File = /usr/local/etc/odbc.ini, Section = NetezzaSQL
readDSNConfig:   Server = 
readDSNConfig:   Database = 
readDSNConfig:   Schema = 
readDSNConfig:   Port = 5480
readDSNConfig:   ReadOnly = 0
readDSNConfig:   BitOneZero = 0
readDSNConfig:   FastSelect = 0
readDSNConfig:   StripCrLf = 0
readDSNConfig:   LegacySQLTables = 0
readDSNConfig:   NumericAsChar = 0
readDSNConfig:   ShowSystemTables = 0
readDSNConfig:   securityLevel = preferredUnSecured
readDSNConfig:   Security level requested = 0
readDSNConfig:   CA certificate used = 
readDSNConfig:   LoginTimeout = 0
readDSNConfig:   QueryTimeout = 0
readDSNConfig:   DateFormat = 1
readDSNConfig:   loadMaxErrors = 1
readDSNConfig:   loadRoundFraction = 0
readDSNConfig:   User ID = 
readDSNConfig:   Workstation Name = 
readDSNConfig:   Application Name = 
readDSNConfig:   Acct String = 
readDSNConfig:   Program Info = 
readDSNConfig:   IgnoreFloatingPointTruncation = 0
    Conn_connect:   Entering    do_password = 0
Connection Error: Function = 'Conn_connect', Line = 1504, State = 'HY000', number = 33, Msg = 'Server and/or port attributes are empty'
...

I've tried to use a DSN or to change the order of the values in the connection string. For instance,I started the string with a semicolon and in the log changed to show this: connStrIn='(;'. If I leave the connection string blank or make a invalid one, dotnet complaints with System.Data.Odbc.OdbcException (0x80131937): ERROR [IM002] [unixODBC][Driver Manager]Data source name not found, and no default driver specified so, I can tell that the "Driver" or "Dsn" parts are being used at first, but seems that are not being passed/received properly by the driver.
I've also created a [NetezzaSQL] section with the connection parameters on the file odbc.ini but here it fails with an Invalid user ID error (in spite that i've provided it with the key "Username"). Nevertheless having a file based DSN is not appropriate as credentials can't be stored in the container for obvious security reasons.

If I access the container and try the nzodbcsql with the connection params or a defined DSN, it connects just fine and allows me to run queries happily.

The driver version I'm using is Release 7.2.0.0 [Build 40845].
The NPS version is: 11.02.0001 Release 11.2.1.0 [Build 30].

This is my Dockerfile:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-bionic AS base

USER root
RUN apt-get update && apt-get -y -qq install make gcc libc6-i386 zlib1g unixodbc unixodbc-dev


COPY netezza.tar.gz /opt/

RUN tar -zxvf /opt/netezza.tar.gz -C /opt/
RUN /opt/netezza/linux64/unpack -f /usr/local/nz

RUN sed -i 's/DebugLogging=false/DebugLogging=true/g' /etc/odbcinst.ini
COPY .odbc.ini /etc/odbc.ini

RUN echo '/usr/local/nz/lib' >> /etc/ld.so.conf.d/x86_64-linux-gnu.conf
RUN echo '/usr/local/nz/lib64' >> /etc/ld.so.conf.d/x86_64-linux-gnu.conf
RUN ldconfig
RUN ln -s /usr/local/nz/bin64/nzodbcsql /usr/local/bin/nzodbcsql

USER 1001
ENV PATH=$PATH:/usr/local/nz/bin64/:
ENV NZ_ODBC_INI_PATH=/etc/
ENV ODBCINI=/etc/odbc.ini
WORKDIR /app


FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build
WORKDIR /src
COPY ["testprg.csproj", "appsettings.json", "appsettings.Development.json", "Program.cs", "./"]
RUN dotnet restore "testprg.csproj"
#COPY . . 
WORKDIR /src
RUN dotnet build "testprg.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "testprg.csproj" -c Release -o /app

FROM base AS final
workdir /app
COPY --from=publish /app .

ENTRYPOINT ["dotnet", "testprg.dll"]

And here is my test program:

using System;
using System.Data.Odbc;

namespace testprg
{
    public class Program
    {
        public static void Main(string[] args)
        {
           String connString = "Driver={NetezzaSQL};Servername=*****;Port=5480;Database=*****;Username=****;Password=*****";
           OdbcConnection conn = new OdbcConnection(connString.Normalize());
           
           OdbcCommand cmd = conn.CreateCommand();
           cmd.CommandText="SELECT COUNT(*) FROM TEST_TABLE;";
           cmd.CommandType=System.Data.CommandType.Text;
           Console.OutputEncoding = System.Text.Encoding.Unicode;
           try {
               Console.WriteLine(conn.ConnectionString);
               conn.Open();
               OdbcDataReader reader = cmd.ExecuteReader();
               while (reader.Read()) {
                   Console.WriteLine(reader["COUNT"].ToString());
               }
           } catch (OdbcException e) {
               Console.WriteLine(e);
           }

        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

青衫儰鉨ミ守葔 2025-01-19 23:52:43

从上面的调试日志中我们看到 /usr/local/etc/odbc.ini 是 Netezza 驱动程序尝试读取的 ini 文件的位置。它没有必填字段。因为您可以使用 nzodbcsql 进行连接,所以驱动程序能够与 NPS 通信,但驱动程序管理器(即 UnixODBC)可能无法识别它。 UnixODBC,大多数时候从 $HOME 目录读取隐藏文件 .odbc.ini。请验证 UnixODBC 是否可以识别已安装的 Netezza ODBC 驱动程序。您可以使用以下命令检查 ini 配置来执行此操作。还尝试使用 isql cli(由 unixODBC 提供)连接数据库。

odbcinst -j
odbcinst -q -s -n NZSQL
odbcinst -q -d -n NetezzaSQL
isql NZSQL username password -v

如果您仍然不成功,请将上述命令的输出粘贴给我。

问候,
萨米尔.

From the above debug log we see /usr/local/etc/odbc.ini is the location of the ini file which the Netezza driver is trying to read & it doesn't have the required fields. Because you are able to connect using nzodbcsql, driver is able to talk to NPS but driver manger i.e. UnixODBC might not able to recognize it. UnixODBC, most of the time reads from $HOME directory that too a hidden file .odbc.ini. Please verify if the installed Netezza ODBC Driver is recognized by UnixODBC. You can do so by checking the ini configuration using the below commands. Also try to connect database using isql cli (provided by unixODBC).

odbcinst -j
odbcinst -q -s -n NZSQL
odbcinst -q -d -n NetezzaSQL
isql NZSQL username password -v

If you are still unsuccessful paste me the output of the above commands.

Regards,
Samir.

很快妥协 2025-01-19 23:52:43

感谢 @Samir-Sayyed 帮助我离线解决了这个问题。解决这个问题的方法是将以下内容添加到 Dockerfile 中:

++RUN sed -i 's/UnicodeTranslationOption=utf8/UnicodeTranslationOption=utf16/g' /etc/odbcinst.ini

原因是 .net 的 ODBC dll 假定 Linux 中的代码页为 utf-16。

Thenks to @Samir-Sayyed who helped me resolve this issue offline. What solved the issue was to add the following to the Dockerfile:

++RUN sed -i 's/UnicodeTranslationOption=utf8/UnicodeTranslationOption=utf16/g' /etc/odbcinst.ini

The reason is that .net's ODBC dll assumes that the code pages is utf-16 in Linux.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文