There is already an open DataReader associated with this Command which must be closed first.

Tenía en mi repository este método:

public IEnumerable Lista()
{
   return context.Estudiante
          .OrderBy(o => o.ApellidoPaterno);
}

Y luego desde un controller llamaba este método así:

var estudiantes= repositorioEstudiante.Lista();

Más adelante tenía una línea donde solicitaba esta propiedad: estudiante.Campus

Como la tabla “Estudiante” tiene una relación de uno a muchos con la tabla “Campus” (un campus tiene muchos estudiantes, y un estudiante pertenece únicamente a un campus), entonces al solicitar esa propiedad aparecía este error:

There is already an open DataReader associated with this Command which must be closed first.

Esto ocurre porque debido a lazy loading el método Lista tiene todos los elementos del estudiante, pero no el del campus relacionado. Cuando intentas llamar la propiedad estudiante.Campus se intenta hacer una nueva conexión y por eso ocurre el error.

Para solucionarlo, debes indicar al método Lista que incluya de una vez todos los registros relacionados de campus.

Se hace así:

public IEnumerable Lista()
{
   return context.Estudiante
          .OrderBy(o => o.ApellidoPaterno)
          .Include(e => e.Campus);
}

De esta forma estás haciendo Eager Loading, lo cual genera una sentencia join en la base de datos que carga de una vez todos los registros. Al solicitar la propiedad estudiante.Campus entonces no estás haciendo otra conexión nueva, y desaparece el error.

Para aprender más sobre los tipos de carga que existen checa esta página en la parte donde dice “Lazy, Eager, and Explicit Loading of Related Data”.

Otra solución al problema es activar MARS, pero prefiero la que te puse en este artículo.

A circular reference was detected while serializing an object of type…

Trabajando en una aplicación asp.net MVC code first, tenía una relación de muchos a muchos, no estoy seguro si esto causó el problema, pero al querer ejecutar una simple instrucción linq similar a esta:

return context.Estudiante
       .OrderBy(o => o.Nombre);

estaba obteniendo este error:

A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.Estudiante_CEA0F1…'.

Encontré la solución en este artículo, la cual consiste en establecer un valor en el constructor del DbContext.

Por default lo encuentras así:

public ApplicationDbContext()
 : base("DefaultConnection", throwIfV1Schema: false)
 {
 }

Y tienes que agregar la línea que va entre las llaves:

public ApplicationDbContext()
 : base("DefaultConnection", throwIfV1Schema: false)
 {
    this.Configuration.ProxyCreationEnabled = false;
 }

Y listo, ya con esto se elimina el error mencionado.

P.D. Si tu problema se debe a que estás tratando de regresar un JsonResult con el serializador que viene con ASP.NET, entonces te recomiendo que en vez de seguir la solución anterior, mejor utilices Json.Net. Puedes añadir la librería así:

using Newtonsoft.Json;

Y ver ejemplos aquí.

Cómo proteger todas las páginas de tu sitio web con AuthorizeAttribute

En aplicaciones Web Forms puedes utilizar web.config para proteger diferentes rutas a páginas o a carpetas de tu aplicación. Esto funciona bien porque estás protegiendo páginas, que a final de cuentas son archivos físicos y conoces la ruta exacta por medio de la cual la gente las visitará.

En ASP.NET MVC contamos con Controllers que pueden ser accedidos mediante diferentes rutas. Aunque todavía es posible proteger estas rutas con el archivo Web.config no se recomienda hacerlo porque es muy probable que pases por alto alguna de las diferentes formas en que se accede un controlador específico. Tarde o temprano cometerás algún error, dejando tu aplicación vulnerable.

La forma de proteger un controlador es mediante el atributo Authorize, por ejemplo:

[Authorize]
public class LocutorController : Controller
{
   // GET: Locutor
   public ActionResult Index()
   {
      return View();
   }
}

O bien, puedes proteger únicamente una acción:

public class LocutorController : Controller
{
   // GET: Locutor
   [Authorize]
   public ActionResult Index()
   {
      return View();
   }
}

Otra opción es permitir el acceso para ciertos usuarios o roles:

[Authorize(Users = "Juan, Pedro")]
public ActionResult Index()
{
   return View();
}

[Authorize(Roles = "Admin, Editor")]
public ActionResult IndexDos()
{
   return View();
}

Si tu aplicación es primordialmente de acceso público y solamente tienes que proteger unos cuantos controladores, esto funciona bien.

¿Pero que pasa si tu aplicación es completamente cerrada? ¿Por ejemplo un panel de control para administrar el seguimiento de clientes y cotizaciones? En ese caso tendrías que recordar escribir estos atributos en todos tus controladores, y cuando añadas uno nuevo meses después y olvides hacerlo, quedará desprotegido.

En este caso puedes usar la lógica contraria: Proteger todo excepto el inicio de sesión.

Para proteger todo, fíjate primero como en el archivo Global.asax de tu aplicación aparece FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);.

Vamos a agregar algo en la función RegisterGlobalFilters, para lo cual debes abrir el archivo FilterConfig.cs que aparece en la carpeta App_Start.

Añade AuthorizeAttribute usando la línea 4:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
   filters.Add(new HandleErrorAttribute());
   filters.Add(new System.Web.Mvc.AuthorizeAttribute());
}

Esto protegerá todos tus controladores. Para abrir el acceso a algunos de ellos (en nuestro ejemplo únicamente los controladores de login) hazlo así:

[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
   ViewBag.ReturnUrl = returnUrl;
   return View();
}

De hecho verás que cuando creas una aplicación con Visual Studio ya están añadidos estos atributos en las acciones Login y Register del controlador AccountController.

Ahora ya no es necesario utilizar [Authorize] en cada controlador o acción, todas están protegidas. Solamente tendrás que hacerlo para utilizar roles o usuarios como se describe arriba.

Cómo encontrar el Id de un registro recién insertado en una tabla de SQL Server

En una aplicación asp.net algunas veces necesitas encontrar el Id del registro recién insertado para utilizarlo en otra operación.

En este ejemplo voy a suponer que tienes un DAL y un BLL, es decir, un archivo con terminación .xsd y un archivo con terminación .vb.

En el “TableAdapter” de tu archivo xsd crea una nueva consulta y pon lo siguiente:

INSERT INTO [MiTabla] ([Campo1], [Campo2])
OUTPUT INSERTED.IdMiTabla
VALUES (@Campo1, @Campo2)

Nota la segunda línea, ésta hace que el valor de salida de tu consulta NO sea el número de registros afectados, sino el valor del Id del registro recién insertado.

Pero para que esto funcione falta una cosa más: en tu archivo xsd selecciona la consulta en tu “TableAdapter” y abre sus propiedades. Ahora establece la propiedad “ExecuteMode” a “Scalar”, así:

scalar

Listo, suponiendo que a tu “TableAdapter” le diste el nombre “InsertarYRegresarElUltimoId”, ahora en tu BLL utiliza un código similar a este:

Dim UltimoId As Integer = Adapter.InsertarYRegresarElUltimoId(Campo1, Campo2)

El valor guardado en la variable “UltimoId” será el correspondiente al Id recién insertado y ahora lo puedes usar para las demás operaciones que vas a hacer.