2

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.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Jamie Dixon
  • 53,019
  • 19
  • 125
  • 162
  • 1
    It depends on the contents of the loop and the `IEnumerable`. – SLaks Sep 01 '11 at 18:22
  • What is the implementation type of the `IEnumerable`, do you have dode for it? What code is in the loop? – Albin Sunnanbo Sep 01 '11 at 18:24
  • I've added the loop code to my question if that helps? – Jamie Dixon Sep 01 '11 at 18:24
  • 1
    dependencies contains 31 IDependency's – Jamie Dixon Sep 01 '11 at 18:26
  • How do you populate dependencies? – Shiraz Bhaiji Sep 01 '11 at 18:26
  • 2
    More to the point, what's the exact type of `dependencies` and how is it otherwise accessed? – Adam Maras Sep 01 '11 at 18:26
  • The exact type of dependencies is IEnumerable – Jamie Dixon Sep 01 '11 at 18:27
  • @Jamie: What is the actual, runtime type of the object dependencies is referencing? – Reed Copsey Sep 01 '11 at 18:29
  • 2
    Okay... perhaps I worded that comment poorly. The object referenced by `dependencies` must be an instance of a concrete type (like a `List` or `IDependency[]`) as opposed to an interface. We need to know what that concrete type is. – Adam Maras Sep 01 '11 at 18:29
  • I've included the method signature of the method containing the foreach, if that's any help. – Jamie Dixon Sep 01 '11 at 18:30
  • I use the interfaces everywhere and in this case IDependency resolves to the concrete type Dependency (public class Dependency : IDependency). The method that returns this concrete type yields new Dependency{[...]} and has a return type of IEnumerable – Jamie Dixon Sep 01 '11 at 18:32
  • 1
    It's not any help. You need to debug the application and tell us what the runtime type of `dependencies` is. You can either run to that point and inspect the variable in the debugger, or add a piece of code like `string typeName = dependencies.GetType().ToString();` in the method to get the string representation of the type. – Adam Maras Sep 01 '11 at 18:33
  • The typeName is "GraphVisCIDependencyPrototype.Models.CiWebService+d__14" – Jamie Dixon Sep 01 '11 at 18:36
  • The method signature of GetAllReleated[...] is private IEnumerable GetAllRelatedResourcesByParentGuidWithoutCacheCheck(Guid parentCiGuid, Func> getResources) – Jamie Dixon Sep 01 '11 at 18:37
  • did you happen to override the Compare or Equals methods in your objects? I think it may used by the enumerator to loop over the enumerables so if there's some custom code in there then this might prevent correct enumeration. try putting a breakpoint in your override. – mtijn Sep 01 '11 at 18:37
  • Why is this question tagged with `asp.net-mvc-3`? Removing unnecessary tags. – Darin Dimitrov Sep 01 '11 at 18:38
  • There's no overriding going on. – Jamie Dixon Sep 01 '11 at 18:39
  • 1
    We need to see the contents of `GetAllRelatedResourcesByParentGuidWithoutCacheCheck` to see how the `IEnumerable` is being created. – Adam Maras Sep 01 '11 at 18:39
  • @Darin - I included the mvc-3 tag because it's an MVC-3 app I'm working on and wasn't sure if knowing it was mvc-3 would be of some use to someone. – Jamie Dixon Sep 01 '11 at 18:39
  • Is this a custom collection with your own implementation of GetEnumerator? – Adam Tuliper Sep 01 '11 at 18:40
  • @Jamie Dixon, no it's irrelevant. This is standard C# code. Now to the point: where does this `dependencies` variable come from? – Darin Dimitrov Sep 01 '11 at 18:40
  • Thanks Adam. I've included that method in my question. – Jamie Dixon Sep 01 '11 at 18:41
  • 4
    Could it be that by the time you actually do the enumeration, some of the setup (remember, this is lazily evaluated) is no longer valid? For instance, the delegate you pass to it, can it still do its work by the time you start the enumeration? – Lasse V. Karlsen Sep 01 '11 at 18:43
  • Hmm. Calling ToList() on dependencies works so I'd have thought all the work that needs to be done is still valid. Wouldn't the foreach do something similar to what ToList() is doing on the IEnumerable? – Jamie Dixon Sep 01 '11 at 18:45
  • I've added the method calls down the chain to the point where the concrete type Dependency is yield return'd – Jamie Dixon Sep 01 '11 at 18:52
  • 1
    It would be similar, but not identical. I recommend you step through the code (without `.ToList()`) in the debugger, line-by-line, to try to figure out if and where there's a logic error that's being exacerbated by the fact that you're using `yield return` instead of more conventional means (like standard collections or LINQ.) – Adam Maras Sep 01 '11 at 18:52
  • are you sure no one exception is throwed when you create a new dependency or you access the xml? – Blau Sep 01 '11 at 18:58
  • I've just been stepping through the code, and at the end of all of this it now seems to be working as expected. I've not changed the code but for some reason it's now looping over the collection where before it was stepping over it. I'm going to keep testing to see if there's a circumstance where it steps over again. Thanks everyone for all your help, it's been invaluable. – Jamie Dixon Sep 01 '11 at 19:09
  • Ok..it's late so I'm going to go home before I confuse myself into a coma. It started working because the cache had a copy of the generated dot file, not because it's working. Clearning the cache reveals the same issue so I'll keep testing tomorrow until I figure out what's going on. Thanks again everyone for you time, i really do appreciate it. – Jamie Dixon Sep 01 '11 at 19:14
  • If you've got time when you've sorted it, I'm sure there's 20 or so people here who'd like to know what was wrong :) – Russ Cam Sep 01 '11 at 19:25
  • @Russ & everyone-whos-interested: I've added an answer below which _seems_ to be what causes this problem. As I continue debugging throughout the day I'll update my answer if/when required. – Jamie Dixon Sep 02 '11 at 12:51

1 Answers1

2

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.

Jamie Dixon
  • 53,019
  • 19
  • 125
  • 162
  • `IEnumerable` by itself won't timeout. If there was infinite recursion, it would (most likely) throw `StackOverflowException`. Maybe `allResources` could timeout, depending on how is that implemented. – svick Sep 02 '11 at 16:37