为什么FxCop在没有一次性字段的类中提出错误“拥有一次性字段的类型应该是一次性的”?

时间:2009-08-02 15:08:34

标签: linq idisposable fxcop

我有一个LINQ对象,并添加了一个额外的方法。该类没有一次性属性或方法,但FxCop提出错误“拥有一次性字段的类型应该是一次性的”并引用该类。

我已经减少了代码,但仍然收到错误:

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;
        }
    }
}

但是,如果我删除from子句中的EITHER,FxCop会停止给出错误:

partial class WikiPage
{
    public PagePermissionSet GetUserPermissions(Guid? userId) {
        using (WikiTomeDataContext context = new WikiTomeDataContext()) {
            var permissions =
                from pageTag in context.VirtualWikiPageTags
                select new {};

            return null;
        }
    }
}

或者

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不是一次性的。

这是假阳性吗?或者LINQ代码是否以某种方式在类中生成一次性字段?如果它不是误报,FxCop建议我实现IDisposable接口,但我会在Dispose方法中做什么?

编辑: 完整的FxCop错误是:

  

“在'WikiPage'上实现IDisposable,因为它   创建以下IDisposable类型的成员:   'WikiTomeDataContext'。如果'WikiPage'以前有过   发货,添加实现IDisposable的新成员   这种类型被认为是对现有的重大改变   消费者“。

编辑2: 这是引发错误的反汇编代码:

public PagePermissionSet GetUserPermissions(Guid? userId)
{
    using (WikiTomeDataContext context = new WikiTomeDataContext())
    {
        ParameterExpression CS$0$0001;
        ParameterExpression CS$0$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$0$0001 = Expression.Parameter(typeof(Wiki), "wiki") }), Expression.Lambda(Expression.New((ConstructorInfo) methodof(<>f__AnonymousType8..ctor), new Expression[0], new MethodInfo[0]), new ParameterExpression[] { CS$0$0001 = Expression.Parameter(typeof(Wiki), "wiki"), CS$0$0003 = Expression.Parameter(typeof(VirtualWikiPageTag), "pageTag") }));
        return null;
    }
}

编辑3: 似乎有一个包含对DataContext的引用的闭包类。这是它的反汇编代码:

[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
    // Fields
    public WikiTomeDataContext context;

    // Methods
    public <>c__DisplayClass1();
}

4 个答案:

答案 0 :(得分:3)

我的猜测是两个From子句生成对SelectMany的调用,并在数据上下文中使用闭包。闭包的实例有一个datacontext的字段,它导致FxCop警告。这没什么好担心的。

您的datacontext只有一个实例,您可以通过using块清理它。因为闭包没有终结器,所以FxCop警告中没有性能或安全含义。

答案 1 :(得分:2)

我注意到这是一个部分类。您是否检查了该类的其他实现文件,并查看它是否具有未被丢弃的IDisposable成员?

我不认为生成的闭包在这里有问题。使用某些属性生成闭包,这些属性应导致FxCop忽略此类警告。

编辑

OP的进一步调查显示,这是一个IDisposable字段被提升到关闭的问题。

不幸的是,你可以做很多事情。没有办法使闭包实现IDisposable。事件,如果你可以在闭包实例上无法调用IDisposable。

解决此问题的最佳方法是重写代码,使得在闭包中不会捕获一次性值。一次性字段在完成后应始终处理,并在关闭时捕获它会阻止您执行此操作。

答案 2 :(得分:1)

如果您从方法返回LINQ查询,消费者将使用foreach迭代结果。

当一个消费者完成一个foreach循环时,它在内部调用IEnumerable源上的dispose(在这种情况下,你的LINQ查询)。这将处理WikiTomeDataContext。

但是,如果消费者调用方法返回LINQ查询但从未迭代过结果,那么似乎永远不会丢弃可枚举(即,直到垃圾收集器清理对象)。这将导致您的WikiTomeDataContext在垃圾收集之前不会被处理。

你可以解决这个问题的一种方法是在你的LINQ查询结果上调用.ToArray,在你的上下文中调用dispose,然后返回数组。

答案 3 :(得分:0)

提供错误的代码使用WikiDataContext。

您的两个不提供错误的示例使用WikiTomeDataContext。

这两个导致错误的可能存在一些差异。