在C#项目中访问Oracle数据库,本质是通过ADO.NET提供的托管驱动建立一条到数据库实例的网络会话,随后借助命令对象执行SQL。与SQL Server不同,Oracle官方推荐使用Oracle Data Provider for .NET(简称ODP.NET),它能完整支持Oracle特有类型和高性能批量操作。

一、环境准备与驱动选择
开发前首先要决定使用哪种数据访问组件。早期微软曾提供过System.Data.OracleClient,但该组件已被废弃且不支持新版本Oracle特性。目前主流方案是Oracle官方的ODP.NET,分为托管版(Oracle.ManagedDataAccess)和非托管版。托管版无需在部署机器安装Oracle客户端,只需NuGet引用即可,非常适合现代云环境。
通过Visual Studio的NuGet包管理器搜索Oracle.ManagedDataAccess并安装,会在项目自动添加引用。相比非托管版依赖本地OCI库,托管版纯C#实现,部署简单。如果项目历史包袱重,也可以继续使用非托管ODP.NET,但连接字符串中往往要配置TNS_NAMES或完整描述符。
二、配置连接字符串
连接字符串定义了数据库地址、服务名、账号和密码。对于托管驱动,最简单的方式是在App.config或Web.config中写入如下节点,这样代码里只需读取名称。
<configuration>
<connectionStrings>
<add name="OracleConn"
connectionString="Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orcl)));User Id=scott;Password=tiger;"
providerName="Oracle.ManagedDataAccess.Client" />
</connectionStrings>
</configuration>
上述配置使用了TNS描述方式直接内联地址,也可以改用EZCONNECT格式如192.168.0.1:1521/orcl。注意User Id和Password明文写在配置中存在安全风险,生产环境应结合Wallet或密钥管理服务。读取时用ConfigurationManager.ConnectionStrings["OracleConn"].ConnectionString即可。
三、建立连接与查询数据
使用OracleConnection对象时,务必放在using语句中,确保断开后连接被回收进连接池。下面示例展示如何执行一条简单查询并读取结果。
using System;
using System.Data;
using Oracle.ManagedDataAccess.Client;
class Program
{
static void Main()
{
string connStr = "Data Source=192.168.0.1:1521/orcl;User Id=scott;Password=tiger;";
using (OracleConnection conn = new OracleConnection(connStr))
{
try
{
conn.Open();
string sql = "SELECT empno, ename FROM emp WHERE deptno = :deptId";
using (OracleCommand cmd = new OracleCommand(sql, conn))
{
cmd.Parameters.Add(new OracleParameter("deptId", 10));
using (OracleDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine(reader.GetInt32(0) + " " + reader.GetString(1));
}
}
}
}
catch (OracleException ex)
{
Console.WriteLine("Oracle错误:" + ex.Message);
}
}
}
}
代码中参数使用冒号前缀:deptId,这是Oracle绑定变量的标准写法,能有效防止SQL注入。OracleDataReader以只进方式读取,适合大数据量遍历。如果只需填充内存表,也可使用OracleDataAdapter填充DataSet。
异常处理块捕获OracleException,可细分错误号做重试或日志记录。连接池默认开启,短时间反复Open和Close不会真正断开物理连接,而是复用,因此不要手动维护长连接。
四、参数化插入与事务
写入数据同样推荐参数化。以下例子演示带事务的插入,保证多条语句原子性。
using (OracleConnection conn = new OracleConnection(connStr))
{
conn.Open();
OracleTransaction tx = conn.BeginTransaction();
try
{
OracleCommand cmd = conn.CreateCommand();
cmd.Transaction = tx;
cmd.CommandText = "INSERT INTO emp (empno, ename, deptno) VALUES (:no, :name, :dn)";
cmd.Parameters.Add(new OracleParameter("no", 9001));
cmd.Parameters.Add(new OracleParameter("name", "TEST"));
cmd.Parameters.Add(new OracleParameter("dn", 20));
cmd.ExecuteNonQuery();
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
}
事务对象绑定到命令后,只有Commit才会持久化。任何异常都应Rollback以免锁表。Oracle的NUMBER类型映射到C#的decimal或int需留意精度,若列定义NUMBER(10)用GetInt32没问题,但NUMBER(20)可能溢出。
五、常见坑与优化建议
很多连接失败源于防火墙阻断1521端口或SERVICE_NAME写错。可在命令行用tnsping等效工具验证。另外,托管驱动默认语句缓存开启,重复执行同构SQL性能更好,但不要拼接字符串代替绑定变量,否则缓存失效且易被注入。
对于高并发服务,建议在连接字符串加Max Pool Size=100控制上限,并设Connection Lifetime避免僵死连接。若查询返回海量数据,使用OracleDataReader配合FetchSize调整每次网络往返行数,能显著降低延迟。
| 驱动类型 | 部署依赖 | 适用场景 |
|---|---|---|
| 托管ODP.NET | 无客户端 | 云、容器、轻量发布 |
| 非托管ODP.NET | Oracle客户端 | 旧系统、特殊OCI特性 |
| System.Data.OracleClient | .NET Framework | 仅遗留兼容,不推荐 |
综上,C#连接Oracle并不复杂,选对驱动、写好连接串、用绑定变量和事务,就能构建稳定数据层。后续可进一步研究EF Core的Oracle提供程序,将对象映射与查询抽象化。
C#OracleOracleConnection修改时间:2026-08-01 16:24:30