谁能帮我解决这个StackOverFlow.Exception?



我有一个Web应用程序,使用c#/mvc/ef/unitofworkPattern制作。 我有包含两个表的数据库,PERSON 和 CASENOTE。 一个人可以有多个案例笔记,但一个案例笔记可能只有一个人。 PERSON中的密钥是PasNo。 这是 CASENOTE 中的外键,但由于原因,它未在 DDL 中定义为外键。

因此,我有一个搜索页面,有人可以在其中将数据输入到CASENOTE编号文本框中,并取回链接到它可能找到的任何案例注释的所有人。 因此,他们可以只输入部分案例注释,它将返回包含该部分字符串的所有案例注释,然后在每个案例注释中使用 PasNo 找到所有具有这些案例注释的人。

所以这是我的代码:

public IEnumerable<PERSON> GetSearchResults(SearchViewModel viewModel)
{
var people = unitOfWork.PersonRepository.GetAll();
if (viewModel.Casenote != null)
{
var casenotes = unitOfWork.CasenoteRepository.GetAll()
.Where(x => x.CASENOTE1.Trim()
.ToLower()
.Contains(viewModel.Casenote.Trim().ToLower()))
.Take(1000)
.ToList();
if (casenotes.Count > 0)
{
foreach (var casenote in casenotes)
{
var pasint = casenote.PAS_INT_NO;
people = people.Where(w => w.PAS_INT_NO == pasint);
//it gives me the stackoverflow.exception at execution of the above line
} 
}
}
return people.Take(1000).ToList().OrderBy(x => x.SURNAME).ThenBy(x => x.FORENAMES);
}

谁能帮助我,告诉我我做错了什么? 如果需要,我可以提供更多信息。

编辑!我忘了提一下,当我在 CASENOTE 文本框中输入一个字母时,我得到堆栈溢出异常,但是当我输入超过 1 个字母时,我没有,搜索有效。

如果您需要使用条件peopleorw.PAS_INT_NO == pasint中进行选择,这将起作用:

public IEnumerable<PERSON> GetSearchResults(SearchViewModel viewModel)
{
var people = unitOfWork.PersonRepository.GetAll();
if (viewModel.Casenote != null)
{
var casenoteNos = unitOfWork.CasenoteRepository.GetAll()
.Where(x => x.CASENOTE1.Trim()
.ToLower()
.Contains(viewModel.Casenote.Trim().ToLower()))
.Select(casenote => casenote.PAS_INT_NO)
.Take(1000)
.ToList();
if (casenoteNos.Any())
{
people = people.Where(w => casenoteNos.Contains(w.PAS_INT_NO));
}
}
return people.Take(1000).ToList().OrderBy(x => x.SURNAME).ThenBy(x => x.FORENAMES);
}

描述

原始代码中的罪魁祸首是:

people = people.Where(w => w.PAS_INT_NO == pasint)

由于Where产生的IEnumerable没有具体化,因此在这里我们最多创建1000个链接Where,因此最终people变得people.Where(...).Where(...).Where...1000 times

如果在此处检查Where扩展的来源,首先Where生成WhereEnumerableIterator对象,然后Where,随后Where通过调用 CombinePredicates 在此处生成嵌套的 lambda 调用,即:

static Func<TSource, bool> CombinePredicates<TSource>(Func<TSource, bool> predicate1, Func<TSource, bool> predicate2) {
return x => predicate1(x) && predicate2(x);
}

被调用 1000 次变成:

(x=> ...1000 times... (x => predicate1(x) && predicate2(x)) && predicate2(x)) ...1000 times... ) && predicate2(x)

然后,1000 个嵌套调用产生了堆栈溢出异常。

结论:不要链上一吨Where,它不是为此而设计的。无论如何,这里的链式Where(大量AND过滤器(不是想要的,而是需要IN/Contains

您是否有理由在函数顶部获得所有人的列表?我对 EF 不是很熟悉,所以这个语法可能是错误的,但我假设这比让数据库中的每个人都进入然后过滤该列表要有效得多:

foreach (var casenote in casenotes)
{
var pasint = casenote.PAS_INT_NO;
people = unitOfWork.PersonRepository.Where(w => w.PAS_INT_NO == pasint);
//it gives me the stackoverflow.exception at execution of the above line
} 

不确定这是否会解决问题,但是如果您的数据库中有很多人和很多案例注释,这肯定会导致 SO 异常。

最新更新