locked
Should IEnumerable.Count Before a "foreach" Enumeration Cause a Single Enumeration Error RRS feed

  • Question

  • Wanting a count of the returned data elements of a data query, I attempted to add the Count method in the code section below, but when reaching the foreach iteration encountered the error “Only a single enumeration is supported by this IEnumerable.” Without the Count code section, the foreach functions properly.

    try
    {
        if (deviceToken != null)
        {
            Devices_context = result.AsyncState as IdentitiesModel.IdentityServices;
            response = Devices_context.EndExecute<IdentitiesModel.Device>(result);
        }
        else
        {
            DataServiceQuery<IdentitiesModel.Device> query = result.AsyncState as DataServiceQuery<IdentitiesModel.Device>;
            response = query.EndExecute(result);
        }
    
        if (response.Count<IdentitiesModel.Device>() == 0)
        {
            App.verifierID = " ";
            App.PCL.MainPage = new NavigationPage(new BadVerifierIDEntry(App.RequestSource + 2));
        }
    
        foreach (IdentitiesModel.Device device in response)
        {
            if (device.ICID == App.ReqICID)
            {
                activeURI = device.URI;
                SendVerificationNotice();
            }
        }
    }

    Thursday, May 14, 2020 6:06 PM

All replies

  • It's likely that this particular IEnumerable does not support calling Reset on its IEnumerator to restart a new enumeration.

    You can refactor the code as follows, to achieve the same result with a single enumeration:

        bool anyItems = false;
        foreach (IdentitiesModel.Device device in response)
        {
            anyItems = true;
            if (device.ICID == App.ReqICID)
            {
                activeURI = device.URI;
                SendVerificationNotice();
            }
        }
    
        if (!anyItems)
        {
            App.verifierID = " ";
            App.PCL.MainPage = new NavigationPage(new BadVerifierIDEntry(App.RequestSource + 2));
        }

    Thursday, May 14, 2020 7:55 PM
  • If the enumeration doesn't support more than one pass then force it to evaluate using one of the `To` methods. Then you can enumerate as much as you want. It'll take up more memory but that is the cost of working with LINQ.

    var items = response.ToList(); //Or ToArray()
    
    var count = items.Count();
    
    foreach (var item in items)
    {
    };

    Note that unless you really need the total # of items in the enumerable then you shouldn't be using `Count` to see if there are any items. In your current code you are trying to enumerate the items twice. Since you just care about whether there are any items or not use `Any` instead. It is more efficient.

    var items = response.ToList();
    if (items.Any())
    {
       foreach(...)
    } else
    {
       App.verifierID = ...
    };
    
    
    


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, May 15, 2020 3:15 PM
  • I will keep your suggestion in mind. TKS.
    Friday, May 15, 2020 6:47 PM
  • I will keep your suggestion in mind. TKS.
    Friday, May 15, 2020 6:48 PM
  • I posted this issue here because why have the method if it's not really usable? What is the limiting factor that prevents re-enumeration?
    Friday, May 15, 2020 6:51 PM
  • - an IEnumerable does not need to end, in which case a Count can not return a result
    f.e. an infinite number of random numbers:
      var fiveRanoms = Randoms(0, 100).Take(5);
      Console.WriteLine(string.Join(",", fiveRanoms));
    
        
    
        static IEnumerable<int> Randoms(int min, int max)
        {       
                var rn = new Random();            
                while(true)
                    yield return rn.Next(min, max);       
        }
    
    - an IEnumerable can encapsulate a network stream: you can not simply rewind and replay (or you would need to buffer, wasting memorry)
    Saturday, May 16, 2020 9:56 AM