Отслеживание ошибки переполнения стека в моем запросе LINQ - PullRequest
4 голосов
/ 20 апреля 2010

Я написал следующий LINQ запрос:

IQueryable<ISOCountry> entries =
  (from e in competitorRepository.Competitors
   join c in countries on e.countryID equals c.isoCountryCode
   where !e.Deleted
   orderby c.isoCountryCode
   select new ISOCountry() { isoCountryCode = e.countryID, Name = c.Name }
  ).Distinct();

Цель состоит в том, чтобы получить список стран, представленных конкурентами, найденными в системе. 'country' - это массив объектов ISOCountry, явно созданных и возвращенных как IQueryable (ISOCountry - это объект только из двух строк, isoCountryCode и Name). Конкуренты - это IQueryable , который связан с таблицей базы данных через LINQ to SQL , хотя я создал объекты с нуля и использовал декораторы отображения данных LINQ.

По какой-то причине этот запрос вызывает переполнение стека, когда система пытается его выполнить. Я понятия не имею, почему, я попытался обрезать Distinct, возвращая анонимный тип двух строк, используя 'select c', но все это приводит к переполнению. Значение e.CountryID заполняется из раскрывающегося списка, который сам по себе был заполнен из IQueryable , поэтому я знаю, что значения являются подходящими, но даже если нет, я не ожидал бы переполнения стека.

Почему происходит переполнение или почему оно может происходить?

По запросу код для ISOCountry:

public class ISOCountry
{
    public string isoCountryCode { get; set; }
    public string Name { get; set; }
}

Он инициализируется из статического служебного класса следующим образом:

    public static IQueryable<ISOCountry> GetCountryCodes()
    {
        // ISO 3166-1 country names and codes from http://opencountrycodes.appspot.com/javascript
        ISOCountry[] countries = new ISOCountry[] {
            new ISOCountry { isoCountryCode= "AF", Name= "Afghanistan"},
            new ISOCountry { isoCountryCode= "AX", Name= "Aland Islands"},
            new ISOCountry { isoCountryCode= "AL", Name= "Albania"},
            new ISOCountry { isoCountryCode= "DZ", Name= "Algeria"},
            new ISOCountry { isoCountryCode= "AS", Name= "American Samoa"},
            ...
            new ISOCountry { isoCountryCode= "YE", Name= "Yemen"},
            new ISOCountry { isoCountryCode= "ZM", Name= "Zambia"},
            new ISOCountry { isoCountryCode = "ZW", Name = "Zimbabwe"}
        };
        return countries.AsQueryable();
    }
<Ч />

Как я, наконец, заставил его работать, см. Ниже ... Мне все еще интересно, что конкретно не так с исходным запросом, я уверен, что я делал подобные вещи раньше.

IList<string> entries = competitorRepository.Competitors.Select(c=>c.CountryID).Distinct().ToList();
IList<ISOCountry> countries = Address.GetCountryCodes().Where(a => entries.Contains(a.isoCountryCode)).ToList();

1 Ответ

2 голосов
/ 09 мая 2010

Возможно, я сумасшедший, но ваш служебный класс не должен выводить список IQueryable. Вы создаете локальную последовательность, которая выглядит как , она должна быть запрашиваемой. В конечном итоге списки IQueryable должны быть удалены вашим текстовым контентом. Если служебный класс создает список, он должен быть возвращен как (наиболее вероятно) массив или IEnumerable, например:

    public static readonly ISOCountry[] CountryCodes = new ISOCountry[] {
        new ISOCountry { isoCountryCode= "AF", Name= "Afghanistan"},
        new ISOCountry { isoCountryCode= "AX", Name= "Aland Islands"}
        ...
    };

Локальная последовательность может использоваться только в операторе IQueryable .Contains (). Итак, если вы хотите «связать» вашу локальную последовательность с вашей последовательностью IQueryable, вы должны заставить IQueryable запустить оператор SQL и извлечь записи, которые он представляет, из базы данных. Для этого все, что вам нужно сделать, это перебрать записи IQueryable некоторым образом:

IList<Competitor> competitorRecords =  competitorRepository
   .Competitors
   .Where(m => !m.Deleted)
   .OrderBy(m => m.countryId)
   .ToList(); //This fires the SQL statement

Как только вы поймали записи из базы данных, вы можете создать свой список записей ISOCountry. Опять же, поскольку этот список не приходит из вашего текста данных, он не должен быть списком IQueryable. Вместо этого попробуйте это:

IList<ISOCountry> = competitorRecords
    .Join(CountryCodes, key1 => key1.countryId, key2 => key2.isoCountryCode, (competitors, codes) => new ISOCountry { isoCountryCode = competitors.countryId, Name = codes.Name })
    .ToList();

Это будет работать, но вы, вероятно, получаете ненужные записи из базы данных. Было бы еще лучше, если бы вы могли загрузить свой список ISOCountry в базу данных. Как только вы это сделаете, вы сможете запустить запрос так, как он изначально задумывался.

...