2

我有一个 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 子句中的任何一个,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

4 回答 4

3

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

您的数据上下文只有一个实例,您可以通过 using 块对其进行清理。因为闭包没有终结器,所以在 FxCop 警告中没有性能或安全含义。

于 2009-08-02T16:07:40.703 回答
2

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

我认为生成的闭包在这里没有错。闭包是使用某些属性生成的,这些属性会导致 FxCop 忽略这样的警告。

编辑

OP 的进一步调查表明,这是一个 IDisposable 字段被提升为闭包的问题。

不幸的是,您对此无能为力。没有办法使闭包实现 IDisposable。事件如果可以,则无法在闭包实例上调用 IDisposable。

解决这个问题的最好方法是重写你的代码,使得一次性值不会在闭包中被捕获。一次性字段应始终在完成后处理,并在闭包中捕获它会阻止您这样做。

于 2009-08-02T16:43:42.240 回答
1

如果你从你的方法返回一个 LINQ 查询,消费者将使用 foreach 遍历结果。

当消费者完成一个 foreach 循环时,它会在内部调用 IEnumerable 源(在本例中为您的 LINQ 查询)上的 dispose。这将处理 WikiTomeDataContext。

但是,如果消费者调用了返回 LINQ 查询的方法,但从未迭代结果,则似乎永远不会释放 enumerable(也就是说,直到垃圾收集器清理对象)。这将导致您的 WikiTomeDataContext 在垃圾收集之前不会被处理。

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

于 2009-08-02T17:36:44.217 回答
0

您给出错误的代码使用 WikiDataContext。

您没有给出错误的两个示例使用 WikiTomeDataContext。

也许这两者之间存在一些差异导致错误。

于 2009-08-02T15:43:24.880 回答