C# 使用Linq2Sql上下文强制立即执行IEnumerable查询

C# 使用Linq2Sql上下文强制立即执行IEnumerable查询,c#,performance,linq-to-sql,ienumerable,C#,Performance,Linq To Sql,Ienumerable,我已经阅读了大量关于“可能的多重枚举”问题的帖子。我想我理解延迟执行与立即执行的概念,以及返回接口与具体类型的含义 因此,给定下面的数据访问层方法和测试代码,我试图强制立即执行查询ToList()在数据访问层方法中起作用,但在主方法中不起作用(可能是因为在释放上下文后调用了ToList())。将转换为ReadOnlyCollection(或IReadOnlyCollection)也不起作用 static void Main(string[] args) { var

我已经阅读了大量关于“可能的多重枚举”问题的帖子。我想我理解延迟执行与立即执行的概念,以及返回接口与具体类型的含义

因此,给定下面的数据访问层方法和测试代码,我试图强制立即执行查询
ToList()
在数据访问层方法中起作用,但在主方法中不起作用(可能是因为在释放上下文后调用了
ToList()
)。将
转换为ReadOnlyCollection
(或
IReadOnlyCollection
)也不起作用

    static void Main(string[] args)
    {
        var foo = GetItems(i => i.SupiCode.Contains("TestCode")).ToList(); // ObjectDisposedException (context)
    }

    private static IEnumerable<Item> GetItems(Func<Item, bool> filter)
    {
        using (var ctx = new RRPClassesDataContext())
        {
            return ctx.Item.Where(filter); //.ToList(); <-- this works
        }
    }
static void Main(字符串[]args)
{
var foo=GetItems(i=>i.SupiCode.Contains(“TestCode”)).ToList();//ObjectDisposedException(上下文)
}
私有静态IEnumerable GetItems(Func筛选器)
{
使用(var ctx=new RRPClassesDataContext())
{

返回ctx.Item.Where(filter);//.ToList();写入此ctx.Item.Where(filter)linq only将创建linq to sql查询,它将仅在调用ToList()时执行。 如果通过列表进行枚举,则查询将针对DB运行,因为您将作为IEnumerable返回到main类,而ToList()将强制执行查询,并抛出错误


是的,您应该通过ToList()强制执行立即执行.

您看到的问题是由于处理对象上下文的位置造成的。一旦您离开处理它试图针对的对象上下文的
GetItems
方法,您将调用
ToList
。因此,查询将针对已处理的上下文执行,您将得到异常

您可以通过稍微更改代码来验证这一点,如下所示:

static void Main(string[] args)
{
    using (var ctx = new RRPClassesDataContext())
    {
        var foo = GetItems(ctx, i => i.SupiCode.Contains("TestCode"));

        // force execution. context is still open so query works.
        var bar = foo.ToList();
    }
}

private static IEnumerable<Item> GetItems(RRPClassesDataContext ctx, Func<Item, bool> filter)
{
    return ctx.Item.Where(filter);        
}
这是确保查询不会执行多次的唯一方法

是否应修改DAL return.ToList()和/或签名

这取决于应用程序的体系结构。如果DAL将负责打开和关闭数据库连接(即创建/销毁上下文类),那么您别无选择-必须在上下文关闭之前强制执行(以避免上下文处理的异常)

但是,如果您可以保证在方法完成执行后DB上下文保持活动状态(例如,如果每个web请求共享一个上下文类),则不必强制执行

一种方法是将数据上下文类注入到DAL中,类似于:

public class ItemDAL
{
    private readonly RRPClassesDataContext _dataContext;

    public ItemDAL(RRPClassesDataContext dataContext)
    {
        _dataContext = dataContext;
    }

    public IQueryable<Item> GetItems(Func<Item, bool> filter)
    {
        return _dataContext.Items.Where(filter);
    }
}
公共类ItemDAL
{
专用只读RRPClassesDataContext_dataContext;
公共项DAL(RRPClassesDataContext数据上下文)
{
_dataContext=dataContext;
}
公共IQueryable GetItems(Func筛选器)
{
返回_dataContext.Items.Where(过滤器);
}
}

这种方法的缺点是(正如您所暗示的),您不能保证最终不会执行两次查询(因为现在由调用方强制执行查询).

如果您不希望上下文引用从DAL泄漏出去,则必须在GetItems方法内执行查询并返回结果。这与您已经通过.ToList执行的操作相同

在我看来,这也是在您的情况下要做的编写工作。您希望查询立即执行并返回结果,创建上下文实例是一个非常便宜的操作,因此在执行查询时创建它是一个很好的做法。GetItem方法签名可以改为

private static ICollection<Item> GetItems(Func<Item, bool> filter)
    {
        using (var ctx = new RRPClassesDataContext())
        {
            return ctx.Item.Where(filter).ToList(); <-- this works
        }
    }
private静态ICollection GetItems(Func过滤器)
{
使用(var ctx=new RRPClassesDataContext())
{
返回ctx.Item.Where(filter.ToList();
private static ICollection<Item> GetItems(Func<Item, bool> filter)
    {
        using (var ctx = new RRPClassesDataContext())
        {
            return ctx.Item.Where(filter).ToList(); <-- this works
        }
    }