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