¿Por qué FxCop genera el error "Los tipos que poseen campos desechables deberían ser desechables" en una clase sin campos desechables?
-
10-07-2019 - |
Pregunta
Tengo un objeto LINQ con un método adicional agregado. La clase no tiene propiedades o métodos desechables, pero FxCop está generando el error "Tipos que poseen campos desechables deben ser desechables". y haciendo referencia a esa clase.
He reducido el código hasta ahora y sigo recibiendo el error:
partial class WikiPage
{
public PagePermissionSet GetUserPermissions(Guid? userId) {
using (WikiTomeDataContext context = new WikiTomeDataContext()) {
var permissions =
from wiki in context.Wikis
from pageTag in context.VirtualWikiPageTags
select new {};
return null;
}
}
}
Sin embargo, si elimino CUALQUIERA de las cláusulas from, FxCop deja de dar el error:
partial class WikiPage
{
public PagePermissionSet GetUserPermissions(Guid? userId) {
using (WikiTomeDataContext context = new WikiTomeDataContext()) {
var permissions =
from pageTag in context.VirtualWikiPageTags
select new {};
return null;
}
}
}
O
partial class WikiPage
{
public PagePermissionSet GetUserPermissions(Guid? userId) {
using (WikiTomeDataContext context = new WikiTomeDataContext()) {
var permissions =
from wiki in context.Wikis
select new {};
return null;
}
}
}
PagePermissionSet no es desechable.
¿Es esto un falso positivo? ¿O el código LINQ de alguna manera genera un campo desechable en la clase? Si no es un falso positivo, FxCop recomienda que implemente la interfaz IDisposable, pero ¿qué haría en el método Dispose?
EDITAR: El error completo de FxCop es:
" Implementar IDisposable en 'WikiPage' porque crea miembros de los siguientes tipos IDisposable: 'WikiTomeDataContext'. Si 'WikiPage' tiene previamente enviado, agregando nuevos miembros que implementan IDisposable a este tipo se considera un cambio radical a los existentes consumidores. "
Editar 2: Este es el código desmontado que genera el error:
public PagePermissionSet GetUserPermissions(Guid? userId)
{
using (WikiTomeDataContext context = new WikiTomeDataContext())
{
ParameterExpression CS[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public WikiTomeDataContext context;
// Methods
public <>c__DisplayClass1();
}
$0001;
ParameterExpression CS<*>$0003;
var permissions = context.Wikis.SelectMany(Expression.Lambda<Func<Wiki, IEnumerable<VirtualWikiPageTag>>>(Expression.Property(Expression.Constant(context), (MethodInfo) methodof(WikiTomeDataContext.get_VirtualWikiPageTags)), new ParameterExpression[] { CS<*>$0001 = Expression.Parameter(typeof(Wiki), "wiki") }), Expression.Lambda(Expression.New((ConstructorInfo) methodof(<>f__AnonymousType8..ctor), new Expression[0], new MethodInfo[0]), new ParameterExpression[] { CS<*>$0001 = Expression.Parameter(typeof(Wiki), "wiki"), CS<*>$0003 = Expression.Parameter(typeof(VirtualWikiPageTag), "pageTag") }));
return null;
}
}
Editar 3: Parece haber una clase de cierre que contiene una referencia al DataContext. Aquí está su código desmontado:
<*>Solución
Supongo que las dos cláusulas From
generan una llamada a SelectMany
con un cierre en su contexto de datos. La instancia del cierre tiene un campo para el contexto de datos que causa la advertencia de FxCop. No hay de qué preocuparse.
Solo hay una instancia de su contexto de datos, que limpia mediante el bloque de uso. Debido a que el cierre no tiene un finalizador, no hay implicación de rendimiento o seguridad aquí en la advertencia de FxCop.
Otros consejos
Noté que esta es una clase parcial. ¿Ha verificado el otro archivo de implementación para la clase y ha visto si tiene un miembro IDisposable que no se está eliminando?
No creo que el cierre generado sea el culpable aquí. Los cierres se generan con ciertos atributos que deberían hacer que FxCop ignore advertencias como esta.
EDITAR
Una investigación adicional por parte del OP demostró que se trata de un problema con un campo IDisposable que se levanta a un cierre.
Desafortunadamente no hay mucho que puedas hacer al respecto. No hay forma de hacer que el cierre implemente IDisposable. Si pudiera, no hay forma de llamar a IDisposable en la instancia de cierre.
La mejor manera de abordar este problema es reescribir su código de tal manera que no se capture un valor desechable en el cierre. Los campos desechables siempre deben desecharse cuando estén terminados y capturarlos en un cierre le impide hacerlo.
Si devuelve una consulta LINQ desde su método, los consumidores iterarán sobre los resultados usando foreach.
Cuando un consumidor finaliza un bucle foreach, internamente llama a dispose en la fuente IEnumerable (en este caso, su consulta LINQ). Esto eliminará el WikiTomeDataContext.
Sin embargo, si un consumidor realiza una llamada al método que devuelve una consulta LINQ pero nunca repite los resultados, parecería que nunca se eliminaría el enumerable (es decir, hasta que el recolector de basura limpiara el objeto). Esto llevaría a que su WikiTomeDataContext no se elimine hasta la recolección de basura.
Una forma en que podría solucionar este problema es llamando a .ToArray sobre el resultado de su consulta LINQ, llame a dispose en su contexto y luego devuelva la matriz.
Su código que da el error usa WikiDataContext.
Sus dos ejemplos que no dan un error usan WikiTomeDataContext.
Quizás haya alguna diferencia entre estos dos que esté causando el error.