跟踪我的 LINQ 查询中的堆栈溢出错误

Laz*_*rus 5 c# stack-overflow linq-to-objects linq-to-sql

我编写了以下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();
Run Code Online (Sandbox Code Playgroud)

目标是检索系统中找到的竞争对手所代表的国家/地区列表。'countries' 是显式创建的 ISOCountry 对象数组,并作为 IQueryable<ISOCountry> 返回(ISOCountry 是只有两个字符串的对象,isoCountryCode 和 Name)。Competitors 是一个 IQueryable<Competitor>,它通过LINQ to SQL绑定到数据库表尽管我从头开始创建对象并使用 LINQ 数据映射装饰器。

出于某种原因,当系统尝试执行该查询时,该查询会导致堆栈溢出。我不知道为什么,我尝试修剪 Distinct,使用 'select c' 返回两个字符串的匿名类型,但都导致溢出。e.CountryID 值是从下拉列表中填充的,该下拉列表本身是从 IQueryable<ISOCountry> 填充的,所以我知道这些值是合适的,但即使不是,我也不希望堆栈溢出。

为什么会发生溢出或为什么会发生?

根据要求,ISOCountry 的代码:

public class ISOCountry
{
    public string isoCountryCode { get; set; }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

它是从静态实用程序类初始化的,因此:

    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();
    }
Run Code Online (Sandbox Code Playgroud)

我是如何最终让它工作的,见下文......我仍然很好奇原始查询的具体问题,我确定我以前做过类似的事情。

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

Eva*_*gle 2

也许我疯了,但你的实用程序类不应该输出 IQueryable 列表。您正在创建一个看起来应该可查询的本地序列。最终,IQueryable 列表应该由您的数据上下文来挖掘。如果实用程序类正在创建列表,则应(最有可能)以数组或 IEnumerable 形式返回,例如:

    public static readonly ISOCountry[] CountryCodes = new ISOCountry[] {
        new ISOCountry { isoCountryCode= "AF", Name= "Afghanistan"},
        new ISOCountry { isoCountryCode= "AX", Name= "Aland Islands"}
        ...
    };
Run Code Online (Sandbox Code Playgroud)

本地序列只能在 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
Run Code Online (Sandbox Code Playgroud)

从数据库中获取记录后,您可以创建 ISOCountry 记录列表。同样,由于此列表不是来自您的数据上下文,因此它不应该是 IQueryable 列表。相反,试试这个:

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

这可行,但您可能会从数据库中获取不必要的记录。如果您可以将 ISOCountry 列表上传到数据库,那就更好了。完成此操作后,您就可以按照最初设想的方式触发查询。