Asp.Net core Tempdata and redirecttoaction not working
Asked Answered
A

4

39

I have a method in my basecontroller class that adds data to tempdata to display pop-up messages.

protected void AddPopupMessage(SeverityLevels severityLevel, string title, string message)
{
    var newPopupMessage = new PopupMessage()
    {
        SeverityLevel = severityLevel,
        Title = title,
        Message = message
    };
    _popupMessages.Add(newPopupMessage);
    TempData["PopupMessages"] = _popupMessages;
}

If the action returns a view, this works fine. If the action is calling a redirectotoaction I get the following error.

InvalidOperationException: The 'Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.TempDataSerializer' cannot serialize an object of type

Any thoughts ?

Auberon answered 10/6, 2019 at 14:38 Comment(0)
P
59

TempData uses Session, which itself uses IDistributedCache. IDistributedCache doesn't have the capability to accept objects or to serialize objects. As a result, you need to do this yourself, i.e.:

TempData["PopupMessages"] = JsonConvert.SerializeObject(_popupMessages);

Then, of course, after redirecting, you'll need to deserialize it back into the object you need:

TempData["PopupMessages"] = JsonConvert.DeserializeObject<List<PopupMessage>>(TempData["PopupMessages"].ToString());
Picoline answered 10/6, 2019 at 15:26 Comment(7)
This changed totally from asp.net to asp.net core ?Auberon
Yes and no. The difference is that MVC would default to in-memory session storage, which just happens to not require serialization, and then you'd get an exception when you tried to switch to a different session store. While Core defaults to in-memory backing as well, the IDistributedCache interface ensures that all session stores behave the same, i.e. requiring serialization of complex objects.Picoline
In short, you only don't have to serialize when using in-memory sessions, but in-memory sessions should never be used in production. As such, Core actually prepares you for the real world, whereas MVC did not.Picoline
What's wrong with using InMemory sessions in production?Sephira
Memory is RAM, which is volatile. If the app stops or the server it's running on restarts, then everything stored there is gone. Additionally, memory is process restricted. If you run multiple instances of your app such a with containers or a web farm in IIS, then each instance has its own memory allocation, and thus its own session storage.Picoline
If you use in memory sessions in production, you'll have both volatile sessions and no ability to scale, both of which are not good for production.Picoline
Then one can argue, in memory sessions are perfectly fine when your application is not required to scale.Impenitent
D
2

Another scenario where this type of error happened to me was upgrading from AspNetCore 2.2 to 3.0 - and, where I had AzureAD as an OpenID Connect provider - when it would try to process the AzureAD cookie, it threw an exception the TempDataProvider and complained that it could not serialize a type of Int64. This fixed:

    services.AddMvc()
    .AddNewtonsoftJson(options =>
           options.SerializerSettings.ContractResolver =
              new CamelCasePropertyNamesContractResolver());

https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#newtonsoftjson-jsonnet-support

Dancette answered 20/7, 2020 at 13:19 Comment(0)
B
2

I was trying to serialize an Exception object to TempData and ended up having to create my own TempDataSerializer to get it to work (I was migrating some existing code to Core).

// Startup.cs
services.AddSingleton<TempDataSerializer, JsonTempDataSerializer>();

// JsonTempDataSerializer.cs
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;

public class JsonTempDataSerializer : TempDataSerializer
{
    private readonly JsonSerializer _jsonSerializer = JsonSerializer.CreateDefault(new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.All, // This may have security implications
    });

    public override byte[] Serialize(IDictionary<string, object>? values)
    {
        var hasValues = values?.Count > 0;
        if (!hasValues)
            return Array.Empty<byte>();

        using var memoryStream = new MemoryStream();
        using var writer = new BsonDataWriter(memoryStream);

        _jsonSerializer.Serialize(writer, values);

        return memoryStream.ToArray();
    }

    public override IDictionary<string, object> Deserialize(byte[] unprotectedData)
    {
        using var memoryStream = new MemoryStream(unprotectedData);
        using var reader = new BsonDataReader(memoryStream);

        var tempDataDictionary = _jsonSerializer.Deserialize<Dictionary<string, object>>(reader)
            ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

        return tempDataDictionary;
    }
};
Bernadette answered 12/10, 2020 at 18:24 Comment(0)
L
0

In my case, I was getting issues in following script

TempData["Message"] = _localizer["User has been registered successfully"];
return RedirectToAction("Register");

_localizer was returning an object instead of string, so I changed it to following

TempData["Message"] = _localizer["User has been registered successfully"].Value;
return RedirectToAction("Register");

Which fixed the issue (might help someone).

Lydie answered 23/9, 2023 at 15:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.