在企业级应用开发中,与Oracle数据库的交互远不止简单的SELECT查询。当系统复杂度上升,我们需要处理存储过程调用、事务管理以及性能优化等高级场景。本文将带你深入探索如何在.NET Core中使用Oracle.ManagedDataAccess.Core进行高效、安全的数据库操作。
存储过程是Oracle数据库中的强大功能,它允许我们将业务逻辑封装在数据库端,减少网络传输并提高安全性。在C#中调用Oracle存储过程需要特别注意参数传递和结果处理。
首先,我们来看一个简单的存储过程调用示例:
csharp复制using (OracleConnection connection = new OracleConnection(connectionString))
{
connection.Open();
using (OracleCommand command = new OracleCommand("your_procedure_name", connection))
{
command.CommandType = CommandType.StoredProcedure;
// 添加输入参数
command.Parameters.Add("input_param", OracleDbType.Varchar2).Value = "input_value";
// 添加输出参数
command.Parameters.Add("output_param", OracleDbType.Varchar2, ParameterDirection.Output);
command.ExecuteNonQuery();
// 获取输出参数值
string outputValue = command.Parameters["output_param"].Value.ToString();
}
}
Oracle存储过程经常使用游标返回结果集,在C#中处理游标需要特殊技巧:
csharp复制using (OracleConnection connection = new OracleConnection(connectionString))
{
connection.Open();
using (OracleCommand command = new OracleCommand("get_employee_data", connection))
{
command.CommandType = CommandType.StoredProcedure;
// 添加游标参数
OracleParameter cursorParam = new OracleParameter();
cursorParam.ParameterName = "result_cursor";
cursorParam.OracleDbType = OracleDbType.RefCursor;
cursorParam.Direction = ParameterDirection.Output;
command.Parameters.Add(cursorParam);
command.ExecuteNonQuery();
// 获取游标结果
OracleDataReader reader = ((OracleRefCursor)cursorParam.Value).GetDataReader();
while (reader.Read())
{
// 处理每一行数据
}
}
}
在企业应用中,事务管理是确保数据一致性的关键。Oracle.ManagedDataAccess.Core提供了多种事务处理方式。
csharp复制using (OracleConnection connection = new OracleConnection(connectionString))
{
connection.Open();
OracleTransaction transaction = connection.BeginTransaction();
try
{
using (OracleCommand command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandText = "UPDATE accounts SET balance = balance - 100 WHERE account_id = 123";
command.ExecuteNonQuery();
command.CommandText = "UPDATE accounts SET balance = balance + 100 WHERE account_id = 456";
command.ExecuteNonQuery();
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
对于跨数据库或跨资源的事务,可以使用TransactionScope:
csharp复制using (TransactionScope scope = new TransactionScope())
{
using (OracleConnection connection1 = new OracleConnection(connectionString1))
{
connection1.Open();
// 执行第一个数据库操作
}
using (OracleConnection connection2 = new OracleConnection(connectionString2))
{
connection2.Open();
// 执行第二个数据库操作
}
scope.Complete();
}
注意:使用TransactionScope需要确保Oracle数据库服务器配置了分布式事务支持。
与Oracle数据库交互的性能优化涉及多个方面,以下是一些关键点:
Oracle.ManagedDataAccess.Core默认启用连接池,但我们可以根据应用需求调整配置:
csharp复制string connectionString = "Data Source=your_db;User Id=user;Password=pass;" +
"Pooling=true;" +
"Min Pool Size=5;" +
"Max Pool Size=50;" +
"Incr Pool Size=5;" +
"Connection Lifetime=300;" +
"Connection Timeout=30";
使用OracleParameter不仅可以防止SQL注入,还能提高查询性能:
csharp复制using (OracleCommand command = new OracleCommand(
"SELECT * FROM employees WHERE department_id = :dept_id AND salary > :min_salary",
connection))
{
command.Parameters.Add("dept_id", OracleDbType.Int32).Value = departmentId;
command.Parameters.Add("min_salary", OracleDbType.Decimal).Value = minSalary;
using (OracleDataReader reader = command.ExecuteReader())
{
// 处理结果
}
}
对于大量数据操作,批量处理可以显著提高性能:
csharp复制using (OracleCommand command = new OracleCommand(
"INSERT INTO orders (order_id, customer_id, order_date) VALUES (:order_id, :customer_id, :order_date)",
connection))
{
command.ArrayBindCount = 1000;
OracleParameter orderIdParam = new OracleParameter("order_id", OracleDbType.Int32);
orderIdParam.Value = orderIds; // 数组
command.Parameters.Add(orderIdParam);
// 添加其他参数...
command.ExecuteNonQuery();
}
Oracle的大对象类型需要特殊处理:
csharp复制// 写入CLOB
OracleParameter clobParam = new OracleParameter("clob_data", OracleDbType.Clob);
clobParam.Value = "这是很长的文本内容...";
command.Parameters.Add(clobParam);
// 读取CLOB
using (OracleDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
OracleClob clob = reader.GetOracleClob(0);
string content = clob.Value;
}
}
Oracle和.NET的日期时间处理存在差异,需要注意:
csharp复制// 设置Oracle日期参数
OracleParameter dateParam = new OracleParameter("order_date", OracleDbType.Date);
dateParam.Value = DateTime.Now;
command.Parameters.Add(dateParam);
// 从Oracle读取日期
DateTime orderDate = reader.GetOracleDate(0).Value;
完善的错误处理可以大大提高应用稳定性:
csharp复制try
{
// 数据库操作
}
catch (OracleException ex)
{
switch (ex.Number)
{
case 1: // 主键冲突
// 处理逻辑
break;
case 1013: // 用户没有权限
// 处理逻辑
break;
case 12560: // 网络错误
// 处理逻辑
break;
default:
throw;
}
}
让我们通过一个完整的订单处理案例来综合运用上述技术:
csharp复制public bool ProcessOrder(int orderId, int customerId, List<OrderItem> items)
{
using (OracleConnection connection = new OracleConnection(connectionString))
{
connection.Open();
using (OracleTransaction transaction = connection.BeginTransaction())
{
try
{
// 1. 创建订单头
using (OracleCommand command = new OracleCommand("create_order", connection))
{
command.Transaction = transaction;
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("p_order_id", OracleDbType.Int32).Value = orderId;
command.Parameters.Add("p_customer_id", OracleDbType.Int32).Value = customerId;
command.Parameters.Add("p_status", OracleDbType.Varchar2, ParameterDirection.Output);
command.ExecuteNonQuery();
if (command.Parameters["p_status"].Value.ToString() != "SUCCESS")
{
throw new Exception("创建订单失败");
}
}
// 2. 批量插入订单明细
using (OracleCommand command = new OracleCommand(
"INSERT INTO order_items (order_id, item_id, quantity, price) " +
"VALUES (:order_id, :item_id, :quantity, :price)",
connection))
{
command.Transaction = transaction;
command.ArrayBindCount = items.Count;
int[] orderIds = Enumerable.Repeat(orderId, items.Count).ToArray();
int[] itemIds = items.Select(i => i.ItemId).ToArray();
int[] quantities = items.Select(i => i.Quantity).ToArray();
decimal[] prices = items.Select(i => i.Price).ToArray();
command.Parameters.Add("order_id", OracleDbType.Int32).Value = orderIds;
command.Parameters.Add("item_id", OracleDbType.Int32).Value = itemIds;
command.Parameters.Add("quantity", OracleDbType.Int32).Value = quantities;
command.Parameters.Add("price", OracleDbType.Decimal).Value = prices;
command.ExecuteNonQuery();
}
// 3. 更新库存
using (OracleCommand command = new OracleCommand("update_inventory", connection))
{
command.Transaction = transaction;
command.CommandType = CommandType.StoredProcedure;
OracleParameter itemsParam = new OracleParameter("p_items", OracleDbType.Int32);
itemsParam.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
itemsParam.Value = items.Select(i => i.ItemId).ToArray();
command.Parameters.Add(itemsParam);
OracleParameter quantitiesParam = new OracleParameter("p_quantities", OracleDbType.Int32);
quantitiesParam.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
quantitiesParam.Value = items.Select(i => i.Quantity).ToArray();
command.Parameters.Add(quantitiesParam);
command.ExecuteNonQuery();
}
transaction.Commit();
return true;
}
catch
{
transaction.Rollback();
throw;
}
}
}
}