/// <inheritdoc />
        public virtual IDictionary<string, object> LoadTempData(HttpContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            // Accessing Session property will throw if the session middleware is not enabled.
            var session = context.Session;

            var tempDataDictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            byte[] value;

            if (session.TryGetValue(TempDataSessionStateKey, out value))
            {
                using (var memoryStream = new MemoryStream(value))
                using (var writer = new BsonReader(memoryStream))
                {
                    tempDataDictionary = _jsonSerializer.Deserialize<Dictionary<string, object>>(writer);
                }

                var convertedDictionary = new Dictionary<string, object>(
                    tempDataDictionary,
                    StringComparer.OrdinalIgnoreCase);
                foreach (var item in tempDataDictionary)
                {
                    var jArrayValue = item.Value as JArray;
                    var jObjectValue = item.Value as JObject;
                    if (jArrayValue != null && jArrayValue.Count > 0)
                    {
                        var arrayType = jArrayValue[0].Type;
                        Type returnType;
                        if (_tokenTypeLookup.TryGetValue(arrayType, out returnType))
                        {
                            var arrayConverter = _arrayConverters.GetOrAdd(returnType, type =>
                            {
                                return (Func<JArray, object>)_convertArrayMethodInfo
                                    .MakeGenericMethod(type)
                                    .CreateDelegate(typeof(Func<JArray, object>));
                            });
                            var result = arrayConverter(jArrayValue);

                            convertedDictionary[item.Key] = result;
                        }
                        else
                        {
                            var message = Resources.FormatTempData_CannotDeserializeToken(nameof(JToken), arrayType);
                            throw new InvalidOperationException(message);
                        }
                    }
                    else if (jObjectValue != null)
                    {
                        if (!jObjectValue.HasValues)
                        {
                            convertedDictionary[item.Key] = null;
                            continue;
                        }

                        var jTokenType = jObjectValue.Properties().First().Value.Type;
                        Type valueType;
                        if (_tokenTypeLookup.TryGetValue(jTokenType, out valueType))
                        {
                            var dictionaryConverter = _dictionaryConverters.GetOrAdd(valueType, type =>
                            {
                                return (Func<JObject, object>)_convertDictMethodInfo
                                    .MakeGenericMethod(type)
                                    .CreateDelegate(typeof(Func<JObject, object>));
                            });
                            var result = dictionaryConverter(jObjectValue);

                            convertedDictionary[item.Key] = result;
                        }
                        else
                        {
                            var message = Resources.FormatTempData_CannotDeserializeToken(nameof(JToken), jTokenType);
                            throw new InvalidOperationException(message);
                        }
                    }
                    else if (item.Value is long)
                    {
                        var longValue = (long)item.Value;
                        if (longValue < int.MaxValue)
                        {
                            // BsonReader casts all ints to longs. We'll attempt to work around this by force converting
                            // longs to ints when there's no loss of precision.
                            convertedDictionary[item.Key] = (int)longValue;
                        }
                    }
                }

                tempDataDictionary = convertedDictionary;

                // If we got it from Session, remove it so that no other request gets it
                session.Remove(TempDataSessionStateKey);
            }
            else
            {
                // Since we call Save() after the response has been sent, we need to initialize an empty session
                // so that it is established before the headers are sent.
                session.Set(TempDataSessionStateKey, new byte[] { });
            }

            return tempDataDictionary;
        }