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 = GetSupportsHardByPar开发者_如何学JAVAentGuid(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.
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 the foreach
simply skips execution of its contents where as ToList()
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:
If infinite recursion occurs in an IEnumerable it'll simply timeout when attempting to enumerate in a foreach.
Using ToList() on the IEnumerable seems to return as much data as it can before timing out.
精彩评论