c# - usar - problema hashtags instagram
La operaciĆ³n no se puede completar porque se ha eliminado el DbContext (7)
Aquí está intentando ejecutar el objeto IQueryable en DBContext inactivo. su DBcontext ya está eliminado. solo puede ejecutar el objeto IQueryable antes de que se elimine DBContext. Significa que debe escribir a los users.Select(x => x.ToInfo()).ToList()
dentro del alcance
Soy nuevo en EF y estoy tratando de usar un método de extensión que convierta mi User
tipo de base de datos a mi clase de información UserInfo
.
Estoy usando la base de datos primero si eso hace la diferencia?
Mi código a continuación da el error
La operación no se puede completar porque el DbContext ha sido eliminado.
try
{
IQueryable<User> users;
using (var dataContext = new dataContext())
{
users = dataContext.Users
.Where(x => x.AccountID == accountId && x.IsAdmin == false);
if(users.Any() == false)
{
return null;
}
}
return users.Select(x => x.ToInfo()).ToList(); // this line is the problem
}
catch (Exception ex)
{
//...
}
Puedo ver por qué lo haría, pero tampoco entiendo por qué el resultado de la instrucción where no se guarda en el objeto del users
.
Así que supongo que mi pregunta principal es por qué no funciona y, en segundo lugar, ¿cuál es la forma correcta de usar métodos de extensión y EF?
Cambia esto:
using (var dataContext = new dataContext())
{
users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);
if(users.Any())
{
ret = users.Select(x => x.ToInfo()).ToList();
}
}
a esto:
using (var dataContext = new dataContext())
{
return = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false).Select(x => x.ToInfo()).ToList();
}
Lo esencial es que solo desea forzar la enumeración del conjunto de datos de contexto una vez. Deje que la persona que llama trate el escenario de conjunto vacío, como deberían.
Debe recordar que las consultas IQueryable no se ejecutan realmente en el almacén de datos hasta que las enumere.
using (var dataContext = new dataContext())
{
Esta línea de código no hace otra cosa que construir la instrucción SQL
users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);
.Any () es una operación que enumera IQueryable, por lo que el SQL se envía a la fuente de datos (a través de dataContext) y luego las operaciones .Any () se ejecutan en su contra.
if(users.Any() == false)
{
return null;
}
}
Su línea de "problema" es reutilizar el sql construido anteriormente, y luego hacer una operación adicional (.Select ()), que solo agrega a la consulta. Si lo dejaste aquí, no hay excepción, excepto tu línea de problema
return users.Select(x => x.ToInfo()).ToList(); // this line is the problem
llama a .ToList (), que enumera IQueryable, que hace que el SQL se envíe a la fuente de datos a través del dataContext que se utilizó en la consulta LINQ original. Como este dataContext se ha eliminado, ya no es válido y .ToList () arroja una excepción.
Ese es el "por qué no funciona". La solución es mover esta línea de código dentro del alcance de su dataContext.
Cómo usarlo correctamente es otra pregunta con unas pocas respuestas posiblemente correctas que dependen de su aplicación (Formularios vs. ASP.net vs. MVC, etc.). El patrón que esto implementa es el patrón de unidad de trabajo. No hay ningún costo para crear un nuevo objeto de contexto, por lo que la regla general es crear uno, hacer su trabajo y luego deshacerse de él. En las aplicaciones web, algunas personas crearán un contexto por solicitud.
Esta pregunta y respuesta me llevan a creer que IQueryable requiere un contexto activo para su funcionamiento. Eso significa que deberías probar esto en su lugar:
try
{
IQueryable<User> users;
using (var dataContext = new dataContext())
{
users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);
if(users.Any() == false)
{
return null;
}
else
{
return users.Select(x => x.ToInfo()).ToList(); // this line is the problem
}
}
}
catch (Exception ex)
{
...
}
Esto puede ser tan simple como agregar ToList () en su repositorio. Por ejemplo:
public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
using (var ctxt = new RcContext())
{
// causes an error
return ctxt.MyObjects.Where(x => x.MyObjects.Id == id);
}
}
Proporcionará el error dispuesto Contexto Db en la clase de llamada, pero esto se puede resolver ejerciendo explícitamente la enumeración agregando ToList () en la operación LINQ:
public IEnumerable<MyObject> GetMyObjectsForId(string id)
{
using (var ctxt = new RcContext())
{
return ctxt.MyObjects.Where(x => x.MyObjects.Id == id).ToList();
}
}
La razón por la cual se arroja el error es que el objeto está dispuesto y luego estamos tratando de acceder a los valores de la tabla a través del objeto, pero el objeto está dispuesto. Mejor convertir eso en ToList () para que podamos tener valores
Tal vez no obtenga los datos hasta que los use (es una carga lenta), por lo que dataContext no existe cuando intenta hacer el trabajo. Apuesto a que si hicieras ToList () en el alcance estaría bien.
try
{
IQueryable<User> users;
var ret = null;
using (var dataContext = new dataContext())
{
users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false);
if(users.Any())
{
ret = users.Select(x => x.ToInfo()).ToList();
}
}
Return ret;
}
catch (Exception ex)
{
...
}
Los objetos expuestos como IQueryable<T>
e IEnumerable<T>
realidad no se "ejecutan" hasta que se iteran o se accede de otro modo, como por ejemplo, se componen en una List<T>
. Cuando EF devuelve un IQueryable<T>
es esencialmente solo componer algo capaz de recuperar datos, en realidad no está realizando la recuperación hasta que lo consuma.
Puede obtener una idea de esto al colocar un punto de interrupción donde se define .ToList()
, frente a cuando se llama a .ToList()
. (Desde dentro del alcance del contexto de datos como Jofry ha señalado correctamente). El trabajo para extraer los datos se realiza durante la llamada ToList()
.
Por eso, debe mantener IQueryable<T>
dentro del contexto de datos.