How does LINQ defer execution when in a using statement

后端 未结 2 725
孤城傲影
孤城傲影 2021-02-08 01:36

Imagine I have the following:

private IEnumerable MyFunc(parameter a)
{
   using(MyDataContext dc = new MyDataContext)
   {
      return dc.tablename.Select(row          


        
相关标签:
2条回答
  • 2021-02-08 02:06

    I would expect that to simply not work; the Select is deferred, so no data has been consumed at this point. However, since you have disposed the data-context (before leaving MyFunc), it will never be able to get data. A better option is to pass the data-context into the method, so that the consumer can choose the lifetime. Also, I would recommend returning IQueryable<T> so that the consumer can "compose" the result (i.e. add OrderBy / Skip / Take / Where etc, and have it impact the final query):

    // this could also be an instance method on the data-context
    internal static IQueryable<SomeType> MyFunc(
        this MyDataContext dc, parameter a)
    {
       return dc.tablename.Where(row => row.parameter == a);
    }
    
    private void UsingFunc()
    {
        using(MyDataContext dc = new MyDataContext()) {
           var result = dc.MyFunc(new a());
    
           foreach(var row in result)
           {
               //Do something
           }
        }
    }
    

    Update: if you (comments) don't want to defer execution (i.e. you don't want the caller dealing with the data-context), then you need to evaluate the results. You can do this by calling .ToList() or .ToArray() on the result to buffer the values.

    private IEnumerable<SomeType> MyFunc(parameter a)
    {
       using(MyDataContext dc = new MyDataContext)
       {
          // or ToList() etc
          return dc.tablename.Where(row => row.parameter == a).ToArray();
       }
    }
    

    If you want to keep it deferred in this case, then you need to use an "iterator block":

    private IEnumerable<SomeType> MyFunc(parameter a)
    {
       using(MyDataContext dc = new MyDataContext)
       {
          foreach(SomeType row in dc
              .tablename.Where(row => row.parameter == a))
          {
            yield return row;
          }
       }
    }
    

    This is now deferred without passing the data-context around.

    0 讨论(0)
  • 2021-02-08 02:07

    I just posted another deferred-execution solution to this problem here, including this sample code:

    IQueryable<MyType> MyFunc(string myValue)
    {
        return from dc in new MyDataContext().Use()
               from row in dc.MyTable
               where row.MyField == myValue
               select row;
    }
    
    void UsingFunc()
    {
        var result = MyFunc("MyValue").OrderBy(row => row.SortOrder);
        foreach(var row in result)
        {
            //Do something
        }
    }
    

    The Use() extension method essentially acts like a deferred using block.

    0 讨论(0)
提交回复
热议问题