Charlie
Charlie

Reputation: 10307

Strange EF behaviour when passing DbSet to a method with parameter type IEnumerable<>

I have a repository class which is querying the EF DbContext like this:

public IEnumerable<ProductFilterData> GetAllProducts()
{
   return this.DataContext.Products.Select(x => new ProductFilterData { Id = x.Id, Name = x.Name, ProductDetailLevelCode = x.ProductDetailLevel.Code, Description = x.Description, ParentProductIds = x.ParentProducts.Select(p => p.Id) });
}

Which works fine. However, when I refactor this code to this:

public IEnumerable<ProductFilterData> GetAllProducts()
{
    return this.MapToProductFilterData(this.DataContext.Products);
}

private IEnumerable<ProductFilterData> MapToProductFilterData(IEnumerable<Product> products)
{
    return products.Select(x => new ProductFilterData { Id = x.Id, Name = x.Name, ProductDetailLevelCode = x.ProductDetailLevel.Code, Description = x.Description, ParentProductIds = x.ParentProducts.Select(p => p.Id) });
}

I get an exception:

There is already an open DataReader associated with this Command which must be closed first.

Why does passing a reference to the DbSet to a method cause a change in the behavior of EF? Incidentally, if I change the parameter type of MapToProductFilterData to IQueryable then it works. Also, if I remove the relationship mappings it also works.

Upvotes: 1

Views: 1016

Answers (1)

Ladislav Mrnka
Ladislav Mrnka

Reputation: 364249

You must change it to IQueryable because if you pass it as IEnumerable you actually execute query SELECT * FROM Product. Your method starts iterating the result set but your projection calls x.ParentProducts.Select(p => p.Id) which results in lazy loading of ParentProducts navigation property. Lazy loading opens second active data reader which is possible only if you have it enabled in connection string.

Upvotes: 4

Related Questions