Использование IQueryable в соединении вызывает исключение StackOverflow - PullRequest
3 голосов
/ 04 октября 2011

Кто-нибудь знает, почему это вызывает исключение stackoverflow:

public IQueryable<Category> LoadCategories(bool onlyCatsWithProducts, ...)
{
    var db = new DbDataContext();
    var res = db.Categories.AsQueryable();

    if (onlyCatsWithProducts)
        res = from p in db.Products
              from c in res
              where p.CategoryID == c.ID
              select c;
    ...
    return res;
}

Обновление

Изменен пример кода, чтобы было понятно, почему я назначаю переменнуюзатем переназначить его позже.В основном это потому, что я пишу функцию для возврата категорий из базы данных и принимаю несколько параметров (например, onlyCatsWithProducts), где каждый фильтрует набор результатов, только если они имеют значение.Обратите внимание, что это все еще не мой настоящий код, потому что мои запросы более сложные, и я просто хочу показать самый простой запрос, необходимый для воспроизведения ошибки.

Ответы [ 3 ]

3 голосов
/ 05 октября 2011

Майкл, отвечая на свой вопрос, сказал, что он понятия не имел, почему замена порядка его from исправила его проблему.

Подобным образом это вызвало переполнение стека:

var res = db.Categories.AsQueryable();
res = from p in db.Products
      from c in res
      where p.CategoryID == c.ID
      select c;

Как это не так:

var res = db.Categories.AsQueryable();
res = from c in res
      from p in db.Products
      where p.CategoryID == c.ID
      select c;

И вот почему.Эти два вышеупомянутых запроса переводятся компилятором в этот код:

var res = db.Categories.AsQueryable();
var q = db.Products
    .SelectMany(p => res, (p, c) => new { p, c })
    .Where(x => x.p.CategoryID == x.c.ID)
    .Select(x => x.c);

и это соответственно:

var res = db.Categories.AsQueryable();
var q = res
    .SelectMany(c => db.Products, (c, p) => new { c, p })
    .Where(x => x.p.CategoryID == x.c.ID)
    .Select(x => x.c);

Первый содержит лямбду p => res, которая по существу захватываетссылка на переменную res.Поскольку res переназначается при каждом запуске запроса, ссылается на переназначенную версию самого себя и переполнение стека взрыва!

Во втором * res находится вне любого lamdbas, поэтому ссылка не являетсяt захвачено и используется только исходная ссылка - то есть res = db.Categories.AsQueryable(), и это не изменится при выполнении запроса.

Вероятно, было бы так же просто использовать:

var res = from c in db.Categories
          from p in db.Products
          where p.CategoryID == c.ID
          select c;

Надеюсь, это поможет прояснить, что происходит.

0 голосов
/ 05 октября 2011

Извинения, после публикации я обнаружил, что замена заказа, похоже, решает проблему.Не знаю почему, хотя:

var db = new DbDataContext();
var res = db.Categories.AsQueryable();
res = from c in res
      from p in db.Products
      where p.CategoryID == c.ID
      select c;
0 голосов
/ 05 октября 2011

Зачем создавать переменную, а затем сразу же назначать ее заново, используя переменную в присваивании?Ваш запрос, вероятно, выполняет какую-то бесконечную рекурсию, вызывая StackOverFlowException.Попробуйте что-то вроде:

var res = 
  from c in DB.Instance.Categories
  from p in DB.Instance.Products
  where p.CategoryID == c.ID
  select c;

Обновление:

Попробуйте что-то вроде ниже.Я думаю, что вам следует избегать использования res в назначении res.

IQueryable<Category> res;

if (onlyCatsWithProducts)
    res = from p in db.Products
          from c in db.Categories.AsQueryable()
          where p.CategoryID == c.ID
          select c;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...