Следующий код использует Entity Framework 6 и управляемых провайдеров Oracle для вызова хранимой процедуры Oracle, которая возвращает несколько курсоров.
Оператор using
выдает следующее исключение:
System.ObjectDisposedException: 'Cannot access a disposed object.Object name: 'OracleConnection'.'
Если я удалю оператор using и вместо этого использую код в следующем посте. Я не получаю ошибок.
Использование Entity Framework для вызова хранимой процедуры Oracle с несколькими курсорами
Почему оператор using вызывает исключение? Мне было предложено, что есть ошибка с Oracle Managed Provider. Но мои коллеги используют одного и того же провайдера, и их заявления об использовании работают нормально.
Пример кода:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using Oracle.ManagedDataAccess.Client;
using System.Data.Entity.Infrastructure;
namespace MyCompany
{
public class MyClass
{
private MyDbContext _dbContext = new MyDbContext();
public MyItems GetMyItems(string id)
{
var sqlQuery = "";
var oracleParameters = new List<OracleParameter>();
var oneEntityList = new List<OneEntity>();
var twoEntityList = new List<TwoEntity>();
var threeEntityList = new List<ThreeEntity>();
sqlQuery = @"
BEGIN
MY_PACKAGE.GetMyItems(:id, :p_cursor1, :p_cursor2, :p_cursor3);
END;
";
oracleParameters = new List<OracleParameter>
{
new OracleParameter("p_id", id),
new OracleParameter("p_cursor1", OracleDbType.RefCursor, ParameterDirection.Output),
new OracleParameter("p_cursor2", OracleDbType.RefCursor, ParameterDirection.Output),
new OracleParameter("p_cursor3", OracleDbType.RefCursor, ParameterDirection.Output)
};
using (var connection = _dbContext.Database.Connection)
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = sqlQuery;
command.Parameters.AddRange(oracleParameters.ToArray());
using (var reader = command.ExecuteReader())
{
oneEntityList = ((IObjectContextAdapter)dbContext).ObjectContext
.Translate<OneEntity>(reader)
.ToList();
reader.NextResult();
twoEntityList = ((IObjectContextAdapter)dbContext).ObjectContext
.Translate<TwoEntity>(reader)
.ToList();
reader.NextResult();
threeEntityList = ((IObjectContextAdapter)dbContext).ObjectContext
.Translate<ThreeEntity>(reader)
.ToList();
}
return new MyItems { OneEntity = oneEntityList, TwoEntity = twoEntityList, ThreeEntity = threeEntityList };
}
}
}
}
Это правильно и уместно использовать с using
заявлений вокруг одноразовых предметов, когда у вас есть время жизни; однако, в этом случае: нет! Соединение здесь относится к контексту данных, и, предположительно, сам контекст данных является IDisposable
, и оно будет располагать соединением, когда располагается контекст данных.
Итак: хотя вам может быть разрешено заимствовать соединение из контекста данных для выполнения запросов - вы не должны пытаться его здесь разместить. Это может привести к закрытию/удалению соединения в неожиданное время с непредсказуемыми результатами.
И наоборот: если у вас была var conn = new OracleConnection(...)
, тогда вы явно владеете этим соединением (если только вы не var conn = new OracleConnection(...)
его чему-то, что будет управлять временем жизни), и вы должны его утилизировать.
Просто, чтобы еще больше усложнить ситуацию... в настоящее время ваш MyClass
кажется, владеет контекстом db через:
private MyDbContext _dbContext = new MyDbContext();
Поэтому в идеале ваш MyClass
должен быть одноразовым (: IDisposable
), а утилизация MyClass
должна каскадно _dbContext
.