Après avoir discuté avec un collègue, je suis intéressé de savoir si quelqu'un a fait des recherches / tests pour voir les moyens les plus efficaces et les plus recommandés de stocker des données dans la session asp.net.

Je suis récemment tombé sur cette question: Comment y accéder variables de session de n'importe quelle classe dans ASP.NET? et a commencé à utiliser cette technique (création de ma propre classe de session pour me donner l'avantage d'intellisense et éviter le besoin d'un casting explicite lors de la récupération des données de la session).

Cependant, il a été suggéré que les données de mon objet sont sérialisées dans une chaîne, puis stockées ... puis désérialisées lors de la récupération et cela peut ne pas être très efficace.

Par exemple ... Si je veux charger les autorisations actuelles de l'utilisateur et les stocker dans la session; Je peux avoir une propriété dans mon objet de session définie comme List<int> user_permissions puis sur chaque page je peux if (session_class.user_permissions.contains(7))

Alternativement, je pourrais stocker directement dans la session sous forme de chaîne quelque chose comme; HttpContext.Current.Session["user_permissions"] = "[1][4][8][12]" puis sur chaque page je peux vérifier HttpContext.Current.Session["user_permissions"].contains("[7]")

Quelle option serait la plus efficace et est-il possible de quantifier?

1
Lee Tickett 16 juil. 2017 à 19:00

5 réponses

Meilleure réponse

Je pense que nous avons trouvé ce qui semble être une raison valable pour déterminer que le stockage d'objets (en particulier en utilisant la technique de wrapper de classe personnalisée Comment accéder aux variables de session à partir de n'importe quelle classe dans ASP.NET?) n'est pas la meilleure solution. D'après ce que nous comprenons, cela stockerait toutes les propriétés (champs) de la classe en mémoire, même celles qui sont vides.

public class my_session {
 public int iUserID;
 public string sUserName;
 public List<int> lsUSerPermissions;
 public List<TestStatus> lsTestStatuses;
 public int iSomethingElse;
 public bool bSomething;
}

Même si nous ne remplirons / définirons pas toutes les propriétés pour toutes les sessions, chaque session contiendra toutes les propriétés (et consommera la mémoire associée). Au départ, je pensais que c'était étrange, mais maintenant je comprends que cela a du sens, donc si / quand vous définissez chaque propriété, la mémoire est disponible et l'objet n'a pas besoin d'être détruit et recréé ailleurs.

Si nous attribuons directement, nous devons uniquement créer des variables de session pour les propriétés qui doivent être stockées.

Donc, maintenant que nous avons décidé de les stocker individuellement;

Session["iUserID"] = 1;
Session["sUserName"] = "Lee";

Comment aborder les objets que nous «devons» stocker? Il semble que chaque objet doit être examiné individuellement. Est-ce que string sUserPermissions = "[ViewUser][CreateUser][EditUser]" occupe moins de mémoire que List<string> lsUserPermissions = new List<string>() { "[ViewUser]", "[CreateUser]", "[EditUser]" } et est-il plus rapide de sérialiser / désérialiser si nécessaire?

Encore une bonne quantité de recherche nécessaire.

-1
Lee Tickett 24 juil. 2017 à 08:54

En supposant que vous n'hébergez que sur une seule machine (et que l'état de session n'a donc pas besoin d'être sérialisé dans un référentiel commun), vous pouvez utiliser un MemoryCache commun pour héberger vos données de session en mémoire, avec un identifiant de session unique.

public class SessionData
{
    private static readonly MemoryCache Cache = new MemoryCache("SessionData");
    private static readonly CacheItemPolicy Policy = new CacheItemPolicy {SlidingExpiration = new TimeSpan(0, 30, 0)};

    public static SessionData Get(string sessionId)
    {
        if (Cache.Contains(sessionId))
            return Cache[sessionId] as SessionData;

        var item = new SessionData {SessionId = sessionId};
        Cache.Add(sessionId, item, Policy);
        return item;
    }

    public string SessionId { get; set; }
    public string Foo { get; set; }
    public int Bar { get; set; }
}

Exemple d'utilisation:

var item = SessionData.Get(this.Session.SessionID);
item.Foo = "Hello";
item.Bar = 123;

Les données de session sont conservées en mémoire avec une expiration glissante de 30 minutes - à chaque fois qu'on y accède, le délai de 30 minutes est réinitialisé. De cette façon, vous ne souffrirez pas de gonflement de la mémoire des sessions expirées. Essayez-le contre votre implémentation actuelle et voyez comment il se compare.

0
Pete 18 juil. 2017 à 16:47

Il y a deux questions que je découvre dans votre message.

1. Stockage de différents types de données dans la session asp.net

Vous savez peut-être qu'il existe trois modes pour conserver la session dans ASP.NET, comme suit: InProc, StateServer et SqlServer.

En mode InProc, vous pouvez définir n'importe quel objet pour la valeur d'une session sans sérialisation, car la session et le Web s'exécutent dans un même processus.

Mais pour d'autres moyens, la valeur de la session doit être sérialisée lorsque la valeur n'est pas une structure. SateServer et SqlServer s'exécutant sur un processus indépendant (peut-être sur des serveurs indépendants), la valeur de la session sera donc sérialisée et conservée sur un autre processus lorsque vous la définissez.

2. Quelle option serait la plus efficace et est-il possible de quantifier en List.contains() et String.contains()?

Si vous ne disposez pas de données volumineuses dans votre cas, elles ne font pas beaucoup de différence. Si vous voulez être efficace dans contains, vous pouvez essayer HashSet.

0
Johan Shen 19 juil. 2017 à 12:43

La question est en fait de savoir quelle est l'importance des performances de votre méthode par rapport à l'importance d'avoir une base de code maintenable et lisible?

Cela ferait une différence sur le cadre que vous utilisiez pour sérialiser et désérialiser vos données également, cela pourrait valoir la peine de faire des tests de vitesse rudimentaires dessus. Si vous pouvez traiter un quadrillion de requêtes en une seconde, c'est probablement OK.

Votre question demande explicitement "Quelle option serait la plus efficace et est-il possible de quantifier?" Oui. Absolument. Il suffit de lancer un StopWatch (voir System.Diagnostics je crois que c'est le cas) et d'appeler la méthode plusieurs fois en utilisant des données de test, puis de vérifier l'heure du chronomètre.

P.s. Votre question indique une discussion sur la performance exacte de cette classe Session ... D'après mon expérience, dans une application Web, la (dé) sérialisation n'est pas vraiment une préoccupation majeure. C'est le goulot d'étranglement de la base de données et d'autres processus de longue durée qui ralentissent le serveur. Si cette question a été posée à cause de l'argument "le serveur est déjà lent, donc c'est juste extra ..." alors dites-leur de résoudre les autres problèmes, c'est comme blâmer la paille pour avoir brisé le dos du chameau quand c'est le 2 tonned de briques qui l'ont vraiment fait. Juste une intuition, c'est l'argument: P

0
Dan Rayson 20 juil. 2017 à 19:47

La première chose à faire est de déterminer ce qu'ASP.NET est capable de sérialiser prêt à l'emploi. Comme la source de référence ASP.NET est disponible, nous pouvons explorer la sérialisation de l'état jusqu'à cette classe: AltSerialization

Il montre que les types suivants sont pris en charge nativement:

String
Int32
Boolean
DateTime
Decimal
Byte
Char
Single
Double
SByte
Int16
Int64
UInt16
UInt32
UInt64
TimeSpan
Guid
IntPtr
UIntPtr
Null // not really a type :-)

Pour tous les autres types, le système utilise un BinaryFormatter, qui est une classe publique .NET. BinaryFormatter est très facile à utiliser, il connaît de nombreuses classes par défaut, vous pouvez donc faire des benchmarks simples en comparant ce que BinaryFormatter va produire avec ce que vous pouvez vous-même "manuellement".

En général, vous serez toujours en mesure de battre les performances de BinaryFormatter car vous connaissez mieux vos données, mais cela signifie que vous devrez écrire du code de sérialisation / désérialisation supplémentaire. Vous pouvez également créer des classes personnalisées et implémenter ISerializable .

Une dernière note, si vous regardez la source AltSerialization, vous verrez ceci:

internal static void WriteValueToStream(Object value, BinaryWriter writer)
{
    if (known type) { ... }
    else
    {
        ...
        var formatter = new BinaryFormatter();
        if (SessionStateUtility.SerializationSurrogateSelector != null)
        {
            formatter.SurrogateSelector = SessionStateUtility.SerializationSurrogateSelector;
        }

        formatter.Serialize(writer.BaseStream, value);
    }
}

Ce qui signifie que vous pouvez implémenter un hook global pour gérer des types spécifiques dans un endroit central, ce qui peut être utile en termes d'ingénierie, ou pour faire des benchmarks (vous pouvez l'activer ou le désactiver facilement). Cela peut être mieux que de créer des classes personnalisées sérialisables.

1
Simon Mourier 21 juil. 2017 à 08:19