How can I safely loop until there is nothing more

2020-04-30 03:09发布

问题:

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?

回答1:

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


回答2:

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



回答3:

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);