将OrderBy添加到EF查询会导致OutOfMemoryException

时间:2017-09-10 19:16:18

标签: c# entity-framework linq-to-entities

编辑 - 只有在投影前添加OrderBy时才会发生这种情况。如果我在投影后添加它,查询非常快,没有内存不足的问题!我使用Linq Pad来检查gen'ed SQL。当我在投影之前执行订单时,SQL会延长数百行,并且其中的投影数量远远超过之后。

这是一个明显缩短的预投影分类示例

from contact in Contacts
orderby contact.ContactID
let DefaultAddress = contact.Addresses.FirstOrDefault(x => x.IsDefault.HasValue && x.IsDefault.Value)
select new {
    ContactID = contact.ContactID,
    DefaultAddressLine2 = DefaultAddress.Line2
}

同样的例子,但是后期预测

from contact in Contacts
let DefaultAddress = contact.Addresses.FirstOrDefault(x => x.IsDefault.HasValue && x.IsDefault.Value)
select new {
    ContactID = contact.ContactID,
    DefaultAddressLine2 = DefaultAddress.Line2
} into x
orderby x.ContactID
select x

第二个例子产生一个直接的SELECT FROM,只有一个OUTER APPLY用于地址。第一个结果是两个OUTER APPLY。在查询的完整版本中,外部应用的相同“加倍”以指数方式发生,最终会有数百个额外的应用!

ORIGINAL - 我有一个查询,可以沿着这些行返回联系人及其默认地址,电话号码和电子邮件

from contact in Db.Contacts
select new
{
Contact = contact,
DefaultAddress = contact.Addresses.FirstOrDefault(x => x.IsDefault.HasValue && x.IsDefault.Value),
DefaultPhone = contact.Phones.FirstOrDefault(x => x.IsDefault.HasValue && x.IsDefault.Value),
DefaultEmail = contact.Emails.FirstOrDefault(x => x.IsDefault.HasValue && x.IsDefault.Value)
} into withDefaults
select new ContactWithDefaultsModel
{
ContactID = withDefaults.Contact.ContactID,

Surname = withDefaults.Contact.ESurname,
First = withDefaults.Contact.EFirst,

// other contact props

DefaultAddressLine2 = withDefaults.DefaultAddress != null ? withDefaults.DefaultAddress.Line2 : null,
DefaultAddressCityID = withDefaults.DefaultAddress != null ? withDefaults.DefaultAddress.CityID : null,
DefaultAddressStateID = withDefaults.DefaultAddress != null ? withDefaults.DefaultAddress.StateID : null,
DefaultAddressCountryID = withDefaults.DefaultAddress != null ? withDefaults.DefaultAddress.CountryID : null,
DefaultAddressZip = withDefaults.DefaultAddress != null ? withDefaults.DefaultAddress.Zip : null,

// same for default phone/email
}

该查询工作正常,但是当我添加OrderBy时,即使是像OrderBy(x => x.ContactID)这样的简单内容,查询也会以OutOfMemoryException崩溃。

我可以从堆栈跟踪中看到它与查询计划编译器有关,但我看不出原因是什么。这是完整的堆栈跟踪。

at System.Text.StringBuilder.ToString()
at System.Data.Entity.Core.Metadata.Edm.EdmType.get_Identity()
at System.Data.Entity.Core.Metadata.Edm.TypeUsage.BuildIdentity(StringBuilder builder)
at System.Data.Entity.Core.Metadata.Edm.RowType.GetRowTypeIdentityFromProperties(IEnumerable`1 properties, InitializerMetadata initializerMetadata)
at System.Data.Entity.Core.Metadata.Edm.RowType..ctor(IEnumerable`1 properties, InitializerMetadata initializerMetadata)
at System.Data.Entity.Core.Metadata.Edm.TypeUsage.get_ModelTypeUsage()
at System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder.Internal.ArgumentValidation.ValidateProperty(DbExpression instance, String propertyName, Boolean ignoreCase, EdmMember& foundMember)
at System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder.DbExpressionBuilder.PropertyByName(DbExpression instance, String propertyName, Boolean ignoreCase)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.BindingScope.TryResolveVar(Var targetVar, DbExpression& resultExpr)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.ResolveVar(Var referencedVar)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(VarRefOp op, Node n)
at System.Data.Entity.Core.Query.InternalTrees.VarRefOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(ComparisonOp op, Node n)
at System.Data.Entity.Core.Query.InternalTrees.ComparisonOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(ConditionalOp op, Node n)
at System.Data.Entity.Core.Query.InternalTrees.ConditionalOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(FilterOp op, Node n)
at System.Data.Entity.Core.Query.InternalTrees.FilterOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitAsRelOp(Node inputNode)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.BuildProjection(Node relOpNode, IEnumerable`1 projectionVars)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(SingleRowOp op, Node n)
at System.Data.Entity.Core.Query.InternalTrees.SingleRowOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitAsRelOp(Node inputNode)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitApply(Node applyNode, DbExpressionKind applyKind)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(OuterApplyOp op, Node n)
at System.Data.Entity.Core.Query.InternalTrees.OuterApplyOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitAsRelOp(Node inputNode)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitApply(Node applyNode, DbExpressionKind applyKind)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(OuterApplyOp op, Node n)
at System.Data.Entity.Core.Query.InternalTrees.OuterApplyOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitAsRelOp(Node inputNode)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitApply(Node applyNode, DbExpressionKind applyKind)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(OuterApplyOp op, Node n)
at System.Data.Entity.Core.Query.InternalTrees.OuterApplyOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitAsRelOp(Node inputNode)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitApply(Node applyNode, DbExpressionKind applyKind)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.Visit(OuterApplyOp op, Node n)
at System.Data.Entity.Core.Query.InternalTrees.OuterApplyOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator.VisitAsRelOp(Node input…tOp.Accept[TResultType](BasicOpVisitorOfT`1 v, Node n)
at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfT`1.VisitNode(Node n)
at System.Data.Entity.Core.Query.PlanCompiler.CTreeGenerator..ctor(Command itree, Node toConvert)
at System.Data.Entity.Core.Query.PlanCompiler.ProviderCommandInfoUtils.Create(Command command, Node node)
at System.Data.Entity.Core.Query.PlanCompiler.CodeGen.Process(List`1& childCommands, ColumnMap& resultColumnMap, Int32& columnCount)
at System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Compile(List`1& providerCommands, ColumnMap& resultColumnMap, Int32& columnCount, Set`1& entitySets)
at System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Compile(DbCommandTree ctree, List`1& providerCommands, ColumnMap& resultColumnMap, Int32& columnCount, Set`1& entitySets)
at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition..ctor(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, DbInterceptionContext interceptionContext, IDbDependencyResolver resolver, BridgeDataReaderFactory bridgeDataReaderFactory, ColumnMapFactory columnMapFactory)
at System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.Common.DbProviderServices.CreateCommandDefinition(DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.CreateCommandDefinition(ObjectContext context, DbQueryCommandTree tree)
at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Boolean streaming, Span span, IEnumerable`1 compiledQueryParameters, AliasGenerator aliasGenerator)
at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)
at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)
at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content)
at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Owin.HttpMessageHandlerAdapter.<BufferResponseContentAsync>d__13.MoveNext()

2 个答案:

答案 0 :(得分:1)

我不能肯定地说这会有所帮助,但是你给计划编译器做了很多事情,这是不必要的。删除所有冗余,您的查询可能如下所示:

from contact in Db.contacts
let DefaultAddress = contact.Addresses.FirstOrDefault(x => x.IsDefault.Value)
let DefaultPhone = contact.Phones.FirstOrDefault(x => x.IsDefault.Value)
let DefaultEmail = contact.Emails.FirstOrDefault(x => x.IsDefault.Value)
select new contactWithDefaultsModel
{
    contactID = contact.contactID,
    Surname = contact.ESurname,
    First = contact.EFirst,

    // other contact props

    DefaultAddressLine2 = DefaultAddress.Line2,
    DefaultAddressCityID = DefaultAddress.CityID,
    DefaultAddressStateID = DefaultAddress.StateID,
    DefaultAddressCountryID = DefaultAddress.CountryID,
    DefaultAddressZip = DefaultAddress.Zip,

    // same for default phone/email
}

以下是我改变的内容:

  • 将投影移至中间匿名类型,并将其替换为let次调用。
  • 删除了所有null项检查。这可以安全地完成,因为整个表达式被转换为SQL,它没有空引用概念。实际上,SQL具有C#现在也具有的空传播,但没有显式运算符(?)。在这里留下这些空值检查会将它们转换为最终的SQL查询,在那里它们是多余的。

这应该为计划编译器提供更少的代码来咀嚼并希望绕过这个异常。

答案 1 :(得分:0)

出现此问题的原因是您没有足够的内存来分配大的结果。您可以使用使用Quicksort算法的List.Sort对列表进行排序。但是,当然,您的原始列表将被排序

  

Contacts.Sort(x =&gt; x.CompareTo(ContactID));