private static async Task ApplyV3SessionProperties(
            IUpstreamEdFiApiInvoker invoker,
            HttpRequestHeaders headers,
            V3SectionReference source,
            V2SectionReference destination,
            short schoolYearFromRoute)
        {
            var termDescriptorLookupKey = new TermDescriptorKey(source.SchoolId, source.SchoolYear, source.SessionName);

            await Task.Run(() =>
            {
                var value = _termDescriptorByNaturalKey.GetOrAdd(termDescriptorLookupKey, k =>
                                                                 GetTermDescriptorValueFromV3Session(invoker, headers, k, schoolYearFromRoute)
                                                                 .ConfigureAwait(false)
                                                                 .GetAwaiter()
                                                                 .GetResult());

                // Is the retrieved value stale?
                if ((DateTime.Now - value.RetrievedDateTime).TotalSeconds > _cacheDurationSeconds.Value)
                {
                    // Refresh the values
                    TermDescriptorValue ignored;
                    _termDescriptorByNaturalKey.TryRemove(termDescriptorLookupKey, out ignored);

                    value = _termDescriptorByNaturalKey.GetOrAdd(termDescriptorLookupKey, k =>
                                                                 GetTermDescriptorValueFromV3Session(invoker, headers, k, schoolYearFromRoute)
                                                                 .ConfigureAwait(false)
                                                                 .GetAwaiter()
                                                                 .GetResult());
                }

                destination.TermDescriptor = value.TermDescriptor;
            })
            .ConfigureAwait(false);
        }
        private static async Task <TermDescriptorValue> GetTermDescriptorValueFromV3Session(
            IUpstreamEdFiApiInvoker invoker,
            HttpRequestHeaders headers,
            TermDescriptorKey key,
            short schoolYearFromRoute)
        {
            // Get the referenced section by natural key
            var response = await invoker.Get(
                typeof(V3Session),
                headers,
                new[]
            {
                new KeyValuePair <string, string>("schoolId", key.SchoolId.ToString()),
                new KeyValuePair <string, string>("schoolYear", key.SchoolYear.ToString()),
                new KeyValuePair <string, string>("sessionName", key.SessionName),
            },
                schoolYearFromRoute)
                           .ConfigureAwait(false);

            StreamReader sr = new StreamReader(response.ResponseStream);
            string       responseContent = await sr.ReadToEndAsync().ConfigureAwait(false);

            if (response.Status == HttpStatusCode.OK)
            {
                // Get the Session-derived members
                var v3Session = JsonConvert.DeserializeObject <V3Session[]>(responseContent, _serializerSettings)
                                .SingleOrDefault();

                if (v3Session == null)
                {
                    throw new Exception(
                              $"Session with schoolId '{key.SchoolId}', schoolYear '{key.SchoolYear}' and sessionName '{key.SessionName}' was not found in host. Unable to support termDescriptor conversion for v2.5 API client.");
                }

                return(new TermDescriptorValue(
                           VersionConverter.DescriptorV3ToV2("TermDescriptor", v3Session.TermDescriptor),
                           DateTime.Now));
            }

            throw new Exception(
                      $"Error obtaining V3 Session resource during mapping of TermDescriptor: {response.Status} - {responseContent}");
        }