Quantcast
Channel: Entity Framework
Viewing all articles
Browse latest Browse all 10318

Commented Issue: Crash in EF5 with SELECT N+1 query [581]

$
0
0
query:
using(var ctxt = new NWDataContext())
{
var products = ctxt.Products.Where(p => p.ProductName.StartsWith("S"));
foreach(var v in products)
{
Console.WriteLine("{0} {1} {2}", v.ProductName, v.CategoryId.Value, v.OrderDetails.Count());
}
Console.WriteLine(ctxt.Products.Count());
}

Exception:
Exception: System.Data.EntityCommandExecutionException
Message: An error occurred while executing the command definition. See the inner exception for details.
Stacktrace:
at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)
at System.Data.Objects.Internal.ObjectQueryExecutionPlan.Execute[TResultType](ObjectContext context, ObjectParameterCollection parameterValues)
at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Objects.ObjectQuery`1.Execute(MergeOption mergeOption)
at System.Data.Objects.DataClasses.EntityCollection`1.Load(List`1 collection, MergeOption mergeOption)
at System.Data.Objects.DataClasses.EntityCollection`1.Load(MergeOption mergeOption)
at System.Data.Objects.DataClasses.RelatedEnd.Load()
at System.Data.Objects.DataClasses.RelatedEnd.DeferredLoad()
at System.Data.Objects.Internal.LazyLoadBehavior.LoadProperty[TItem](TItem propertyValue, String relationshipName, String targetRoleName, Boolean mustBeNull, Object wrapperObject)
at System.Data.Objects.Internal.LazyLoadBehavior.<>c__DisplayClass7`2.<GetInterceptorDelegate>b__1(TProxy proxy, TItem item)
at System.Data.Entity.DynamicProxies.Product_C0742AFF5E64326E56801688B56EA2BB4A6C059ADFC9C7877F55A10DB2EC4BAE.get_OrderDetails()
at Tester.Program.Main(String[] args) in c:\Temp\generatortest\test1\Tester\Program.cs:line 24
Exception: System.InvalidOperationException
Message: There is already an open DataReader associated with this Command which must be closed first.
Stacktrace:
at System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)
at System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute(String method, SqlCommand command)
at System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)

Seems to occur at the first loop, on the query which pulls the order details:
SELECT [Extent1].[Discount] AS [Discount],
[Extent1].[OrderID] AS [OrderID],
[Extent1].[ProductID] AS [ProductID],
[Extent1].[Quantity] AS [Quantity],
[Extent1].[UnitPrice] AS [UnitPrice]
FROM [dbo].[Order Details] AS [Extent1]
WHERE [Extent1].[ProductID] = @EntityKeyValue1

I've attached the solution I used for testing which gave this exception.
Comments: Hi Diego, IMHO you have two choices. 1) Do as DirectX. DirectX has generic functionality like EF and relies for the execution of the details of this functionality on the hardware and its driver. If the hardware doesn't support the feature, DirectX does one of two things: a) it falls back onto a software equivalent or b) gives up. Each driver / hardware has to signal to DirectX that it can do which functionality so DirectX knows what it can expect from the driver/hardware and what it has to do in software. In this scenario, EF's core starts multiple select queries over an open connection without checking whether an active query is still fetching data. This _requires_ MARS. This means that the driver has to signal EF that it can do MARS or not (like a hw driver has to for DirectX). As EF's logic requires MARS it can check whether the driver supports this. If not, EF can fall back to an alternative (e.g. open another connection or other alternative) or give up and report a detailed error that a feature required wasn't provided/available by the underlying driver and the developer has to rewrite the query. This latter solution is not really ideal for code which has to be portable to more than one DB and it pops up at runtime but so is the current situation. 2) leave the error reporting and checking to the driver: EF core simply starts the second query over the open connection and the driver, as it is the actual code executing the query, knows this. It can check whether MARS is available. If not, (disabled or not supported), the driver can throw a proper exception. At the moment no checking is in place at all, EF simply runs the query, hoping for the best. And when it fails, it keels over with an exception which can mean anything: a bug in EF or a misconfigured connection string or a driver which doesn't support a required feature. Because: this all comes from the fact that the foreach streams results in instead of fetching it in 1 go and enumerating the set returned, but that's totally unknown by simply looking at the code: most O/R mappers (including mine, llblgen) will simply fetch the linq query, and enumerate on the results of that set. The inner query is then a SELECT N+1 through lazy loading or will result in 0. I find it a little surprising that EF streams the data live, as it means the connection to the DB is open longer than necessary, as the time spent in the loop adds to the total time the connection has to stay open to read all data. As the DB and the connection is an expensive resource, it's IMHO not a thing one would want, and which at the same time introduces an issue which is totally unclear to the developer.

Viewing all articles
Browse latest Browse all 10318

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>