How can I safely loop until there is nothing more to do without using a "placeholder" while conditon?

1.5k Views Asked by At

In order to call my Web API method until no more data is returned (I'm fetching it in batches, to keep each result set small, due to the 98-lb-weakling persona of the client (Windows CE handheld device)), I'm using this code:

while (moreRecordsExist)
{
    redemptionsList.redemptions.Clear();
    string uri = String.Format("http://platypus:28642/api/Redemptions/{0}/{1}", lastIdFetched, RECORDS_TO_FETCH);
    var webRequest = (HttpWebRequest)WebRequest.Create(uri);
    webRequest.Method = "GET";

    using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
    {
        if (webResponse.StatusCode == HttpStatusCode.OK)
        {
            var reader = new StreamReader(webResponse.GetResponseStream());
            string s = reader.ReadToEnd();
            var arr = JsonConvert.DeserializeObject<JArray>(s);
            if (null == arr) break;

            foreach (JObject obj in arr)
            {
                id = obj.Value<int?>("Id") ?? 0;
                var _redemptionId = obj.Value<string>("RedemptionId") ?? "";
                var _redemptionItemId = obj.Value<string>("RedemptionItemId") ?? "";
                var _redemptionName = obj.Value<string>("RedemptionName") ?? "";
                double _redemptionAmount = obj.Value<double?>("RedemptionAmount") ?? 0.0;
                var _redemptionDept = obj.Value<string>("RedemptionDept") ?? "";
                var _redemptionSubdept = obj.Value<string>("RedemptionSubDept") ?? "";

                redemptionsList.redemptions.Add(new HHSUtils.Redemption
                {
                    Id = id,
                    RedemptionId = _redemptionId,
                    RedemptionItemId = _redemptionItemId,
                    RedemptionName = _redemptionName,
                    RedemptionAmount = _redemptionAmount,
                    RedemptionDept = _redemptionDept,
                    RedemptionSubDept = _redemptionSubdept,
                });
            } // foreach
        } // if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 2))
    } // using HttpWebResponse
    int recordsAdded = LocalDBUtils.BulkInsertRedemptions(redemptionsList.redemptions);
    totalRecordsAdded += recordsAdded;
    //moreRecordsExist = (recordsToFetch > (totalRecordsAdded));
    lastIdFetched = id;
} // while

This works (if I check webResponse for null, it crashes with a NullReferenceException), but I don't really like my while loop, as it is never reached once moreRecordsExist is assigned false. So, I set the Resharper hounds loose on it to see if it had a better suggestion, but it just tells me, "Expression is always true" for that line, and the suggested "fix" for this is to change it to "while (true)"

I fail to see how that is much of an improvement, though.

Is there a way I can accomplish the same thing with a more sensible construct?

3

There are 3 best solutions below

10
On BEST ANSWER

You break out the loop with a break statement immediately after assigning false to moreRecordsExist variable, so its value will never be false in the beginning of the loop. You can make a change as ReSharper suggests and get rid of moreRecordsExist variable:

while (true)
{
    redemptionsList.redemptions.Clear();
    string uri = String.Format("http://platypus:28642/api/Redemptions/{0}/{1}", lastIdFetched, RECORDS_TO_FETCH);
    var webRequest = (HttpWebRequest)WebRequest.Create(uri);
    webRequest.Method = "GET";

    using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
    {
        if (webResponse.StatusCode == HttpStatusCode.OK)
        {
            var reader = new StreamReader(webResponse.GetResponseStream());
            string s = reader.ReadToEnd();
            var arr = JsonConvert.DeserializeObject<JArray>(s);
            if (arr == null) break;

            foreach (JObject obj in arr)
            {
                id = obj.Value<int?>("Id") ?? 0;
                var _redemptionId = obj.Value<string>("RedemptionId") ?? "";
                var _redemptionItemId = obj.Value<string>("RedemptionItemId") ?? "";
                var _redemptionName = obj.Value<string>("RedemptionName") ?? "";
                double _redemptionAmount = obj.Value<double?>("RedemptionAmount") ?? 0.0;
                var _redemptionDept = obj.Value<string>("RedemptionDept") ?? "";
                var _redemptionSubdept = obj.Value<string>("RedemptionSubDept") ?? "";

                redemptionsList.redemptions.Add(new HHSUtils.Redemption
                {
                    Id = id,
                    RedemptionId = _redemptionId,
                    RedemptionItemId = _redemptionItemId,
                    RedemptionName = _redemptionName,
                    RedemptionAmount = _redemptionAmount,
                    RedemptionDept = _redemptionDept,
                    RedemptionSubDept = _redemptionSubdept,
                });
            } // foreach
        } // if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 2))
    } // using HttpWebResponse
    int recordsAdded = LocalDBUtils.BulkInsertRedemptions(redemptionsList.redemptions);
    totalRecordsAdded += recordsAdded;
    //moreRecordsExist = (recordsToFetch > (totalRecordsAdded));
    lastIdFetched = id;
} // while
1
On

Why not return some metadata with response, e.g. {Pages:10} ? Then you know exactly how many records you're going after.

2
On

Consider using a do..while() loop. This gets you around the "while-loop-is-never-reached" problem... it will always run at least once, no matter what moreRecordsExist is set to.

do
{
    redemptionsList.redemptions.Clear();
    string uri = String.Format("http://platypus:28642/api/Redemptions/{0}/{1}",     lastIdFetched, RECORDS_TO_FETCH);
    var webRequest = (HttpWebRequest)WebRequest.Create(uri);
    webRequest.Method = "GET";

    using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
    {
        if (webResponse.StatusCode == HttpStatusCode.OK)
        {
            var reader = new StreamReader(webResponse.GetResponseStream());
            string s = reader.ReadToEnd();
            var arr = JsonConvert.DeserializeObject<JArray>(s);
            if (arr == null) break;

            foreach (JObject obj in arr)
            {
                id = obj.Value<int?>("Id") ?? 0;
                var _redemptionId = obj.Value<string>("RedemptionId") ?? "";
                var _redemptionItemId = obj.Value<string>("RedemptionItemId") ?? "";
                var _redemptionName = obj.Value<string>("RedemptionName") ?? "";
                double _redemptionAmount = obj.Value<double?>("RedemptionAmount") ?? 0.0;    
                var _redemptionDept = obj.Value<string>("RedemptionDept") ?? "";
                var _redemptionSubdept = obj.Value<string>("RedemptionSubDept") ?? "";

                redemptionsList.redemptions.Add(new HHSUtils.Redemption
                {
                    Id = id,
                    RedemptionId = _redemptionId,
                    RedemptionItemId = _redemptionItemId,
                    RedemptionName = _redemptionName,
                    RedemptionAmount = _redemptionAmount,
                    RedemptionDept = _redemptionDept,
                    RedemptionSubDept = _redemptionSubdept,
                });
            } // foreach
        } // if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 2))
    } // using HttpWebResponse
    int recordsAdded = LocalDBUtils.BulkInsertRedemptions(redemptionsList.redemptions);
    totalRecordsAdded += recordsAdded;
    moreRecordsExist = (recordsToFetch > (totalRecordsAdded));
    lastIdFetched = id;
} while (moreRecordsExist);