2016-01-11 22 views
0

我已阅读了大量有关“可能的多个枚举”问题的帖子。我想我理解延迟与立即执行的概念,以及返回接口与具体类型的含义。使用Linq2Sql上下文强制立即执行IEnumerable查询

因此,考虑到下面的数据访问层方法和测试代码,我试图强制立即执行查询。 ToList()适用于数据访问层方法,但不适用于Main方法(可能因为在处理上下文后调用ToList())。铸造as ReadOnlyCollection<Item>(或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 
     } 
    } 

我的目标是防止多个枚举(即多个数据库访问)。从我阅读的内容来看,我不应该修改DAL以适应客户的需求。相反,客户应正确处理退回的IEnumerable。所以我的问题是:

  • 在这种情况下,客户可以强制立即执行(如果是的话)?
  • 如果DAL返回.ToList()和/或签名被修改?

回答

0

你看到的问题是由于你处置对象上下文的地方。一旦您已经离开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);   
} 

,具体回答你的问题:

您可以通过稍微改变你的代码验证这一点,如下如果是,如何)?

不,你不能“强制”消费者(我认为这是你的“客户”是什么意思?)执行查询,除非你自己强制查询(执行致电ToListToArray等)。请注意,您可以这不违反您的API:

private static IEnumerable<Item> GetItems(Func<Item, bool> filter) 
{ 
    using (var ctx = new RRPClassesDataContext()) 
    { 
     // Forces execution and safely allows the context to be disposed. 
     // Still returns an IEnumerable<Item> so the method contract 
     // is preserved. 
     return ctx.Item.Where(filter).ToList(); 
    } 
} 

这是你可以确保查询没有得到执行多次的唯一途径。

DAL是否应返回.ToList()和/或签名是否被修改?

这取决于您的应用程序的体系结构。如果您的DAL将负责打开和关闭数据库连接(即创建/销毁上下文类),则您别无选择 - 您必须在关闭上下文之前强制执行(以避免上下文处置异常)。但是,如果您可以保证您的数据库上下文在方法执行完成后保持活动状态(例如,如果您为每个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); 
    } 
} 

这种方法的缺点是(因为你已经在暗示),你不能保证你不会最终执行查询两次(如现在直到调用者强制执行查询)。

0

写这篇ctx.Item.Where(过滤器) LINQ只能将创建一个LINQ to SQL的查询,它会被执行,只有当你调用ToList()。 如果您通过List枚举,则查询将针对数据库运行,因为您将IEnumerable返回给主类,并且ToList()强制查询exections抛出错误。

是的,你应该强制立即执行ToList()。

0

如果不希望Context引用从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 
     } 
    } 

这也将解决您的“可能的多个枚举”问题。