0

I have the following code, it stops executing with "StackOverFlow unhandled error".

var Requests= GetList();// return List of Request objects

IQueryable<Order> pos= Enumerable.Empty<Order>().AsQueryable();
if (Requests != null)
 {
   if (Requests.Count > 0)
      {
       var GeneralReq = Requests.Select(loc => loc.Id).ToList();


pos = db.Order.Where(loc => loc.Deleted == false && GeneralReq.Any(a => a == loc.Id));
//HERE, stop executing with StackOverFlow Error.

      }
 }

the problem exactly in here:

pos = db.Order.Where(loc => loc.Deleted == false && GeneralReq.Any(a => a == loc.Id));
2
  • 1
    Just don't assume it is the linq expression that caused it. Exactly where the program runs out of stack space is quite unpredictable, any method call is a candidate. Google "c# how to debug a stackoverflowexception" for good debugging advice. Commented Aug 15, 2018 at 14:46
  • How many records come back in GeneralReq? THe way this is setup, it will have to send in every combination that GeneralReq has in memory as SQL to the database. Commented Aug 15, 2018 at 18:27

1 Answer 1

1

Without knowing what GetList() does the likely problem is that it is returning far too many records to safely inject IDs into your .Any() expression. EF will want to turn that .Any() into a query like:

WHERE orders.OrderId IN (22, 25, 45, 46, 52, 66, ...)

This generally isn't efficient and there are limits to the # of IDs you can pass in. It's better to resolve these criteria as a join. I'm not certain if this can result in a stack overflow so I don't believe the code sample you have is complete because without a .ToList() or similar expression, the IQueryable<Order> pos expression would not have materialized yet.

I would look at the relationships that you can resolve between Requests and Orders. If the Request entity as a reference to an Order, then you can alter GetList() to return an IQueryable<Request> then to get the orders:

IQueryable<Request> requests = GetList();
IQueryable<Order> orders = requests.Select(x => x.Order);

From there you can .Select() the details of the orders that you want, and materialize the resulting data.

IQueryable<Request> requests = GetList();
List<OrderViewModel> orderVMs = requests.Select(x => new OrderViewModel
{
  OrderId = x.Order.OrderId,
  RequestId = x.RequestId,
  CustomerName = x.Customer.Name,
  OrderNumber = x.Order.OrderNumber,
  // ...
}).ToList();

If GetList() can return a significant # or results then use .Take() and .Skip() rather than .ToList() to paginate your results.

Sign up to request clarification or add additional context in comments.

2 Comments

IQueryable<Order> orders = requests.Select(x => x.Order); how can I convert safely, coz I can't convert ICollection to IQueryable ?
From the entity declaration, Orders will be a virtual ICollection<Order>. From EF's perspective, dbContext.Requests.Where(x => ...) will return IQueryable<Request>. From there you can continue to expand on the IQueryable expression which EF will "feed" to SQL Server etc. at the point where you call .ToList(), .Any(), .Count() etc. The process is called deferred execution and is extremely valuable for composing efficient queries. To use effectively you want to defer calling things like .ToList() as late as you can, after all filters/criteria are applied.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.