Speichern Sie komplexe Objekte in TempData

72

Ich habe versucht, Daten nach einer Umleitung an eine Aktion zu übergeben, indem ich TempData wie folgt verwendet habe:

if (!ModelState.IsValid)
{
    TempData["ErrorMessages"] = ModelState;
    return RedirectToAction("Product", "ProductDetails", new { code = model.ProductCode });
}

aber leider schlägt es mit folgender Meldung fehl:

System.InvalidOperationExceptionDas Microsoft.AspNet.Mvc.SessionStateTempDataProvider'kann nicht ein Objekt vom Typ serialisiert 'ModelStateDictionary'zu Sitzungszustand.‘

Ich habe ein Problem im MVC-Projekt in Github gefunden , aber obwohl es erklärt, warum ich diesen Fehler erhalte, kann ich nicht sehen, was eine praktikable Alternative wäre.

Eine Möglichkeit wäre, das Objekt in einen JSON-String zu serialisieren und es dann wieder zu deserialisieren und das zu rekonstruieren ModelState. Ist das der beste Ansatz? Gibt es potenzielle Leistungsprobleme, die ich berücksichtigen muss?

Und schließlich gibt es Alternativen, um komplexe Objekte zu serialisieren oder ein anderes Muster zu verwenden, bei dem keine Verwendung erforderlich ist TempData?

Elolos
quelle
du solltest das nicht tun. Wenn der Modellstatus nicht gültig ist, wird standardmäßig nur dieselbe Ansicht mit dem ungültigen Modell zurückgegeben. Sie sollten also nur View (Modell) zurückgeben. und nicht zur Aktion umleiten
hjgraca
1
Dies ist nur ein Beispiel. Ich suche nach einer Möglichkeit, komplexe Objekte in TempData zu speichern, nicht unbedingt in ModelState. Es kann auch Szenarien geben, in denen Sie Ihrem Rat nicht folgen können. Ich bin damit einverstanden, dass dies die beste Vorgehensweise ist
elolos
1
@hjgraca Der Anwendungsfall für diese Art von Situation ist eine Ansicht mit mehreren Teilansichten zum Hinzufügen und Bearbeiten einer Datenliste. Das Modell für die Ansicht ist beispielsweise eine Liste der Modelle. Anschließend gibt es ein Inline-Add-Formular mit einem eigenen Modell (dessen Fehler zurückgesendet werden müssen). Anschließend wird jedes Element auch inline bearbeitet (jedes hat seine eigenen Fehler) ). Dies ist mit clientseitigen Frameworks wie Angular problemlos möglich, mit Razor jedoch nicht ganz so einfach.
Mike Perrenoud
Zu Ihrer Information, Sie können nicht einfach in JSON serialisieren, da die ModelError-Klasse nicht über die entsprechenden Konstruktoren verfügt. Tatsächlich ist dies das einzige Problemkind, die internen ModelError-Klassen. Daher denke ich, dass die Lösung darin besteht, die KVP zu serialisieren und diese dann zu deserialisieren und wieder hinzuzufügen. Ich werde diese Filter veröffentlichen, wenn ich damit fertig bin.
Mike Perrenoud
@MichaelPerrenoud Ich mache etwas in diese Richtung, um eine benutzerdefinierte Klasse (nicht ModelState) in TempData zu speichern. Ich habe mich nur gefragt, ob es einen besseren Ansatz gibt.
Elolos

Antworten:

134

Sie können die Erweiterungsmethoden folgendermaßen erstellen:

public static class TempDataExtensions
{
    public static void Put<T>(this ITempDataDictionary tempData, string key, T value) where T : class
    {
        tempData[key] = JsonConvert.SerializeObject(value);
    }

    public static T Get<T>(this ITempDataDictionary tempData, string key) where T : class
    {
        object o;
        tempData.TryGetValue(key, out o);
        return o == null ? null : JsonConvert.DeserializeObject<T>((string)o);
    }
}

Und Sie können sie wie folgt verwenden:

Sprich objectAist vom Typ ClassA. Sie können dies mit der oben genannten Erweiterungsmethode wie folgt zum temporären Datenwörterbuch hinzufügen:

TempData.Put("key", objectA);

Und um es abzurufen, können Sie Folgendes tun:

var value = TempData.Get<ClassA>("key") wo valueabgerufen wird vom Typ seinClassA

Saum
quelle
Klappt wunderbar. Liebe Newtonsoft!
Stephen McDowell
1
Ich habe dies auch behoben, indem ich das Hinzufügen des SerializableAttributs zu der Klasse markiert habe, die ich für das Objekt verwendet habe, in das gelegt wurdeTempData
brendonparker
@brendonparker Das hat bei mir nicht funktioniert, hängt wahrscheinlich nur vom Objekt ab. Der obige Code scheint überall kopiert zu sein und IMO führt zwangsläufig zu einem verwirrenden Controller- und View-Setup.
Perustaja
@brendonparker Ich habe das Attribut [Serializable ()] verwendet und ISerializable implementiert, aber es hat nicht funktioniert. Können Sie Ihre Lösung teilen?
Abubakar Riaz
15

Ich kann nicht kommentieren, aber ich habe auch ein PEEK hinzugefügt, das schön ist zu überprüfen, ob es vorhanden ist oder zu lesen und nicht für das nächste GET zu entfernen.

public static T Peek<T>(this ITempDataDictionary tempData, string key) where T : class
{
    object o = tempData.Peek(key);
    return o == null ? null : JsonConvert.DeserializeObject<T>((string)o);
}

Beispiel

var value = TempData.Peek<ClassA>("key") where value retrieved will be of type ClassA
Chris Go
quelle
5

Verwenden von System.Text.Json in .Net Core 3.1 und höher

using System.Text.Json;

    public static class TempDataHelper
    {
        public static void Put<T>(this ITempDataDictionary tempData, string key, T value) where T : class
        {
            tempData[key] = JsonSerializer.Serialize(value);
        }

        public static T Get<T>(this ITempDataDictionary tempData, string key) where T : class
        {
            tempData.TryGetValue(key, out object o);
            return o == null ? null : JsonSerializer.Deserialize<T>((string)o);
        }

        public static T Peek<T>(this ITempDataDictionary tempData, string key) where T : class
        {
            object o = tempData.Peek(key);
            return o == null ? null : JsonSerializer.Deserialize<T>((string)o);
        }
    }
Ajt
quelle