IEnumerable 未在 foreach 中枚举
我的一个 IEnumerable
遇到了以前从未见过的问题。
我有一个集合:
IEnumerable<IDependency> dependencies;
它正在 foreach 循环中使用。
foreach (var dependency in dependencies)
由于某种原因,这个 foreach
不会迭代我的 IEnumerable
并且只是跳到最后。
如果我将 foreach
更改为循环遍历列表,但它似乎工作正常:
foreach (var dependency in dependencies.ToList())
我可以做什么导致这种行为?我以前没有在 IEnumerable
中经历过这种情况。
更新:
这是在我的方法GenerateDotString
中运行的foreach
的完整代码:
foreach (var dependency in dependencies)
{
var dependentResource = dependency.Resource;
var lineColor = (dependency.Type == DependencyTypeEnum.DependencyType.Hard) ? "blue" : "red";
output += labelFormat.FormatWith(dependentResource.Name.MakeDotsafeString(), dependentResource.Name, dependentResource.ResourceType);
output += relationshipFormat.FormatWith(dependentResource.Name.MakeDotsafeString(), currentName, lineColor);
if (dependentResource.DependentResources != null)
{
output += GenerateDotString(dependentResource, dependentResource.DependentResources, searchDirection);
}
}
return output;
更新2:
这是签名包含此 foreach 的方法(如果它有帮助)。
private static string GenerateDotString(IResource resource, IEnumerable<IDependency> dependencies, SearchEnums.SearchDirection searchDirection)
更新 3:
这里是方法 GetAllRelatedResourcesByParentGuidWithoutCacheCheck
:
private IEnumerable<IDependency> GetAllRelatedResourcesByParentGuidWithoutCacheCheck(Guid parentCiGuid, Func<Guid, IEnumerable<IDependency>> getResources)
{
if (!_itemsCheckedForRelations.Contains(parentCiGuid)) // Have we already got related resources for this CI?;
{
var relatedResources = getResources(parentCiGuid);
_itemsCheckedForRelations.Add(parentCiGuid);
if (relatedResources.Count() > 0)
{
foreach (var relatedResource in relatedResources)
{
relatedResource.Resource.DependentResources = GetAllRelatedResourcesByParentGuidWithoutCacheCheck(relatedResource.Resource.Id, getResources);
yield return relatedResource;
}
}
}
}
更新 4:
我在此处添加链中的方法,以便清楚地了解我们如何获取依赖项的集合。
上面的方法 GetAllRelatedResourcesByParentGuidWithoutCacheCheck 接受一个委托,在本例中是:
private IEnumerable<IDependency> GetAllSupportsResources(Guid resourceId)
{
var hardDependents = GetSupportsHardByParentGuid(resourceId);
var softDependents = GetSupportsSoftByParentGuid(resourceId);
var allresources = hardDependents.Union(softDependents);
return allresources;
}
正在调用:
private IEnumerable<IDependency> GetSupportsHardByParentGuid(Guid parentCiGuid)
{
XmlNode ciXmlNode = _reportManagementService.RunReportWithParameters(Res.SupportsHardReportGuid, Res.DependentCiReportCiParamName + "=" + parentCiGuid);
return GetResourcesFromXmlNode(ciXmlNode, DependencyTypeEnum.DependencyType.Hard);
}
并返回:
private IEnumerable<IDependency> GetResourcesFromXmlNode(XmlNode ciXmlNode, DependencyTypeEnum.DependencyType dependencyType)
{
var allResources = GetAllResources();
foreach (var nodeItem in ciXmlNode.SelectNodes(Res.WebServiceXmlRootNode).Cast<XmlNode>())
{
Guid resourceGuid;
var isValidGuid = Guid.TryParse(nodeItem.SelectSingleNode("ResourceGuid").InnerText, out resourceGuid);
var copyOfResource = allResources.Where(m => m.Id == resourceGuid).SingleOrDefault();
if (isValidGuid && copyOfResource != null)
{
yield return new Dependency
{
Resource = copyOfResource,
Type = dependencyType
};
}
}
}
这是返回具体类型的位置。
I'm encountering a problem with one of my IEnumerable
's that I haven't seen before.
I have a collection:
IEnumerable<IDependency> dependencies;
that's being used in a foreach
loop.
foreach (var dependency in dependencies)
For some reason this foreach
doesn't iterate over my IEnumerable
and simply skips to the end.
If I change my foreach
to loop through a a list however it seems to work fine:
foreach (var dependency in dependencies.ToList())
What could I be doing that's causing this behaviour? I haven't experienced this with IEnumerable
before.
Update:
Here's the entire code of my foreach
that's running in my method GenerateDotString
:
foreach (var dependency in dependencies)
{
var dependentResource = dependency.Resource;
var lineColor = (dependency.Type == DependencyTypeEnum.DependencyType.Hard) ? "blue" : "red";
output += labelFormat.FormatWith(dependentResource.Name.MakeDotsafeString(), dependentResource.Name, dependentResource.ResourceType);
output += relationshipFormat.FormatWith(dependentResource.Name.MakeDotsafeString(), currentName, lineColor);
if (dependentResource.DependentResources != null)
{
output += GenerateDotString(dependentResource, dependentResource.DependentResources, searchDirection);
}
}
return output;
Update 2:
Here's the signature of the method containing this foreach (incase it helps).
private static string GenerateDotString(IResource resource, IEnumerable<IDependency> dependencies, SearchEnums.SearchDirection searchDirection)
Update 3:
Here's the method GetAllRelatedResourcesByParentGuidWithoutCacheCheck
:
private IEnumerable<IDependency> GetAllRelatedResourcesByParentGuidWithoutCacheCheck(Guid parentCiGuid, Func<Guid, IEnumerable<IDependency>> getResources)
{
if (!_itemsCheckedForRelations.Contains(parentCiGuid)) // Have we already got related resources for this CI?;
{
var relatedResources = getResources(parentCiGuid);
_itemsCheckedForRelations.Add(parentCiGuid);
if (relatedResources.Count() > 0)
{
foreach (var relatedResource in relatedResources)
{
relatedResource.Resource.DependentResources = GetAllRelatedResourcesByParentGuidWithoutCacheCheck(relatedResource.Resource.Id, getResources);
yield return relatedResource;
}
}
}
}
Update 4:
I'm adding the methods in the chain here to be clear on how we're getting the collection of dependencies.
The above method GetAllRelatedResourcesByParentGuidWithoutCacheCheck
accepts a delegate which in this case is:
private IEnumerable<IDependency> GetAllSupportsResources(Guid resourceId)
{
var hardDependents = GetSupportsHardByParentGuid(resourceId);
var softDependents = GetSupportsSoftByParentGuid(resourceId);
var allresources = hardDependents.Union(softDependents);
return allresources;
}
which is calling:
private IEnumerable<IDependency> GetSupportsHardByParentGuid(Guid parentCiGuid)
{
XmlNode ciXmlNode = _reportManagementService.RunReportWithParameters(Res.SupportsHardReportGuid, Res.DependentCiReportCiParamName + "=" + parentCiGuid);
return GetResourcesFromXmlNode(ciXmlNode, DependencyTypeEnum.DependencyType.Hard);
}
and returns:
private IEnumerable<IDependency> GetResourcesFromXmlNode(XmlNode ciXmlNode, DependencyTypeEnum.DependencyType dependencyType)
{
var allResources = GetAllResources();
foreach (var nodeItem in ciXmlNode.SelectNodes(Res.WebServiceXmlRootNode).Cast<XmlNode>())
{
Guid resourceGuid;
var isValidGuid = Guid.TryParse(nodeItem.SelectSingleNode("ResourceGuid").InnerText, out resourceGuid);
var copyOfResource = allResources.Where(m => m.Id == resourceGuid).SingleOrDefault();
if (isValidGuid && copyOfResource != null)
{
yield return new Dependency
{
Resource = copyOfResource,
Type = dependencyType
};
}
}
}
which is where the concrete type is returned.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
所以看起来问题是与无限依赖于自身的
dependencies
集合有关。从我的调试看来,迭代
IEnumerable
会导致超时,因此foreach
只是跳过其内容的执行,而ToList()
返回为在超时之前尽可能多地进行操作。我的说法可能不正确,但据我所知,情况似乎就是如此。
为了提供一些关于这一切是如何发生的背景知识,我将解释我昨天所做的代码更改。
应用程序要做的第一件事是构建按资源类型过滤的所有资源的集合。这些是通过 Web 服务调用从我们的 CMDB 引入的。
然后我所做的是,对于选择的每个资源(在本例中通过自动完成),我将进行 Web 服务调用并根据其 Guid 获取该资源的依赖项。 (递归地)
我昨天对此进行了更改,以便我们不需要在第二个 Web 服务调用中获取完整的资源信息,而只需在 Web 服务调用中获取 Guid 列表并从我们的资源集合中获取资源。
我忘记的是,对依赖项的 Web 服务调用没有按类型进行过滤,因此它返回的结果在原始资源集合中不存在。
我需要进一步查看,但似乎此时,新的依赖资源集合变得依赖于自身,因此导致
IEnumerable
集合稍后超时。这就是我今天要讲的地方,如果我发现任何其他内容,我一定会在这里注明。
总结一下:
So it looks like the problem was to do with the
dependencies
collection infinately depending on itself.It seems from my debugging that iterating the
IEnumerable
causes a timeout and so theforeach
simply skips execution of its contents where asToList()
returns as much as it can before timing out.I may not be correct about that but it's what seems to be the case as far as I can tell.
To give a bit of background as to how this all came about I'll explain the code changes I made yesterday.
The first thing the application does is build up a collection of all resources which are filtered by resource type. These are being brought in from our CMDB via a web service call.
What I was then doing is for each resource that was selected (via autocomplete in this case) I'd make a web service call and get the dependents for the resource based on its Guid. (recursively)
I changed this yesterday so that we didn't need to obtain the full resource information in this second web service call, rather, simply obtain a list of Guids in the web service call and grab the resources from our resources collection.
What I forgot was that the web service call for dependents wasn't filtered by type and so it was returning results that didn't exist in the original resources collection.
I need to look a bit further but it seems that at this point, the new collection of dependent resources was becoming dependent on itself and thus, causing the
IEnumerable<IDependents>
collection later on to timeout.This is where I've got to today, if I find anything else I'll be sure to note it here.
To summarise this: