private static async Task <ApiCallRequest> BuildGetAPICallRestAsync <TModel>(BaseDataModel <TModel> model, EntityInfo entity, ODataQuery <TModel> oDataQuery, ApiCall apiOverride, bool useLinqGet, bool loadPages) { string getApi = useLinqGet ? entity.SharePointLinqGet : entity.SharePointGet; IEnumerable <EntityFieldInfo> fields = entity.Fields.Where(p => p.Load); Dictionary <string, string> urlParameters = new Dictionary <string, string>(); StringBuilder sb = new StringBuilder(); // Only add select statement whenever there was a filter specified if (entity.SharePointFieldsLoadedViaExpression) { // $select foreach (var field in fields) { // If there was a selection on which fields to include in an expand (via the QueryProperties() option) then add those fields if (field.SharePointExpandable && field.ExpandFieldInfo != null) { AddExpandableSelectRest(sb, field, null, ""); } else { sb.Append($"{JsonMappingHelper.GetRestField(field)},"); } } urlParameters.Add("$select", sb.ToString().TrimEnd(new char[] { ',' })); sb.Clear(); } // $expand foreach (var field in fields.Where(p => p.SharePointExpandable)) { if (entity.SharePointFieldsLoadedViaExpression) { sb.Append($"{JsonMappingHelper.GetRestField(field)},"); // If there was a selection on which fields to include in an expand (via the Include() option) and the included field was expandable itself then add it if (field.ExpandFieldInfo != null) { string path = ""; AddExpandableExpandRest(sb, field, null, path); } } else { if (field.ExpandableByDefault) { sb.Append($"{JsonMappingHelper.GetRestField(field)},"); } } } urlParameters.Add("$expand", sb.ToString().TrimEnd(new char[] { ',' })); oDataQuery.AddODataToUrlParameters(urlParameters, ODataTargetPlatform.SPORest); // REST apis do not apply a default top // In order to not receive all items in one request, we apply a default top // We don't change the original ODataQuery to avoid side effects if (useLinqGet && !urlParameters.ContainsKey(ODataQuery <TModel> .TopKey)) { urlParameters.Add(ODataQuery <TModel> .TopKey, model.PnPContext.GlobalOptions.HttpSharePointRestDefaultPageSize.ToString()); } sb.Clear(); // Build the API call string baseApiCall = ""; if (apiOverride.Equals(default(ApiCall))) { baseApiCall = $"{model.PnPContext.Uri.AbsoluteUri.TrimEnd(new char[] { '/' })}/{getApi}"; } else { baseApiCall = $"{model.PnPContext.Uri.AbsoluteUri.TrimEnd(new char[] { '/' })}/{apiOverride.Request}"; } // Parse tokens in the base api call baseApiCall = await ApiHelper.ParseApiCallAsync(model, baseApiCall).ConfigureAwait(false); sb.Append(baseApiCall); // Build the querystring parameters NameValueCollection queryString = HttpUtility.ParseQueryString(string.Empty); foreach (var urlParameter in urlParameters.Where(i => !string.IsNullOrEmpty(i.Value))) { // Add key and value, which will be automatically URL-encoded, if needed queryString.Add(urlParameter.Key, urlParameter.Value); } // Build the whole URL if (queryString.AllKeys.Length > 0) { // In .NET Framework to ToString() of a NameValueCollection will use HttpUtility.UrlEncodeUnicode under // the covers resulting in issues. So we decode and encode again as a workaround. This code produces the // same result when used under .NET5/Core versus .NET Framework sb.Append($"?{queryString.ToEncodedString()}"); } // Create ApiCall instance and call the override option if needed var call = new ApiCallRequest(new ApiCall(sb.ToString(), ApiType.SPORest, loadPages: loadPages)); if (model.GetApiCallOverrideHandler != null) { call = await model.GetApiCallOverrideHandler.Invoke(call).ConfigureAwait(false); } return(call); }