Partage via


Informations de base relatives à JournaledGrain

Les grains journalisés dérivent de JournaledGrain<TGrainState,TEventBase> avec les paramètres de type suivants :

  • TGrainState représente l’état du grain. Il doit s’agir d’une classe avec un constructeur par défaut public.
  • TEventBase est un supertype commun à tous les événements qui peuvent être déclenchés pour ce grain. Il peut correspondre à n’importe quelle classe ou interface.

Tous les objets d’état et d’événement doivent être sérialisables (car les fournisseurs de cohérence des journaux peuvent être amenés à les conserver et/ou à les envoyer dans des messages de notification).

Pour les grains dont les événements sont des objets POCO (anciens objets C# bruts), JournaledGrain<TGrainState> peut être utilisé en tant que raccourci pour JournaledGrain<TGrainState,TEventBase>.

Lecture de l’état du grain

Pour lire l’état actuel du grain et déterminer son numéro de version, JournaledGrain dispose de propriétés

GrainState State { get; }
int Version { get; }

Le numéro de version est toujours égal au nombre total d’événements confirmés, et l’état est le résultat de l’application de tous les événements confirmés à l’état initial. L’état initial, qui a la version 0 (car aucun événement ne lui a été appliqué), est déterminé par le constructeur par défaut de la classe GrainState.

Important : L’application ne doit jamais modifier directement l’objet retourné par State. Il est destiné à la lecture uniquement. À la place, quand l’application souhaite modifier l’état, elle doit le faire indirectement en déclenchant des événements.

Déclencher des événements

Le déclenchement d’événements s’effectue via l’appel de la fonction RaiseEvent. Par exemple, un grain représentant une conversation peut déclencher PostedEvent pour indiquer qu’un utilisateur a posté un billet :

RaiseEvent(new PostedEvent()
{
    Guid = guid,
    User = user,
    Text = text,
    Timestamp = DateTime.UtcNow
});

Notez que RaiseEvent lance un accès en écriture au stockage, mais n’attend pas la fin de l’écriture. Pour de nombreuses applications, il est important d’attendre que nous ayons une confirmation de la persistance de l’événement. Dans ce cas, nous effectuons toujours un suivi en attendant ConfirmEvents :

RaiseEvent(new DepositTransaction()
{
    DepositAmount = amount,
    Description = description
});
await ConfirmEvents();

Notez que même si vous n’appelez pas explicitement ConfirmEvents, les événements finissent par être confirmés. Cela se produit automatiquement en arrière-plan.

Méthodes de transition d’état

Le runtime met à jour l’état du grain automatiquement chaque fois que des événements sont déclenchés. Il n’est pas nécessaire que l’application mette à jour explicitement l’état après le déclenchement d’un événement. Toutefois, l’application doit toujours fournir le code qui indique comment mettre à jour l’état en réponse à un événement. Cette opération peut être effectuée de deux façons.

(a) La classe GrainState peut implémenter une ou plusieurs méthodes Apply sur StateType. En règle générale, plusieurs surcharges sont créées, et la correspondance la plus proche est choisie pour le type d’exécution de l’événement :

class GrainState
{
    Apply(E1 @event)
    {
        // code that updates the state
    }

    Apply(E2 @event)
    {
        // code that updates the state
    }
}

(b) Le grain peut remplacer la fonction TransitionState :

protected override void TransitionState(
    State state, EventType @event)
{
   // code that updates the state
}

Les méthodes de transition sont supposées n’avoir aucun autre effet secondaire que la modification de l’objet d’état. Elles doivent être déterministes, sinon les effets sont imprévisibles. Si le code de transition lève une exception, celle-ci est interceptée et incluse dans un avertissement, émis par le fournisseur de cohérence des journaux, au sein du journal Orleans.

Le moment précis où le runtime appelle les méthodes de transition dépend du fournisseur de cohérence des journaux choisi et de sa configuration. Les applications ne doivent pas reposer sur un minutage particulier, sauf quand cela est spécifiquement garanti par le fournisseur de cohérence des journaux.

Certains fournisseurs, par exemple le fournisseur de cohérence des journaux Orleans.EventSourcing.LogStorage, relisent la séquence d’événements à chaque chargement du grain. Ainsi, tant que les objets d’événement peuvent toujours être correctement désérialisés à partir du stockage, il est possible de modifier radicalement la classe GrainState et les méthodes de transition. Toutefois, pour les autres fournisseurs, par exemple le fournisseur de cohérence des journaux Orleans.EventSourcing.StateStorage, seul l’objet GrainState est conservé de manière persistante. Les développeurs doivent donc vérifier qu’il peut être correctement désérialisé quand il est lu à partir du stockage.

Déclencher plusieurs événements

Il est possible d’appeler plusieurs fois RaiseEvent avant d’appeler ConfirmEvents :

RaiseEvent(e1);
RaiseEvent(e2);
await ConfirmEvents();

Toutefois, cela peut provoquer deux accès successifs au stockage, et entraîner un risque de défaillance du grain après l’écriture du premier événement uniquement. Il est donc généralement préférable de déclencher plusieurs événements à la fois, à l’aide de

RaiseEvents(IEnumerable<EventType> events)

Ainsi, la séquence d’événements donnée est obligatoirement écrite de manière atomique dans le stockage. Notez que, dans la mesure où le numéro de version correspond toujours à la longueur de la séquence d’événements, le déclenchement de plusieurs événements entraîne une augmentation du numéro de version d’une valeur supérieure à un.

Récupérer la séquence d’événements

La méthode suivante de la classe de base JournaledGrain permet à l’application de récupérer un segment spécifique de la séquence de tous les événements confirmés :

Task<IReadOnlyList<EventType>> RetrieveConfirmedEvents(
    int fromVersion,
    int toVersion);

Toutefois, elle n’est pas prise en charge par tous les fournisseurs de cohérence des journaux. En l’absence de prise en charge ou si le segment spécifié de la séquence n’est plus disponible, NotSupportedException est levé.

Pour récupérer tous les événements jusqu’à la dernière version confirmée, vous devez appeler

await RetrieveConfirmedEvents(0, Version);

Seuls les événements confirmés peuvent être récupérés : une exception est levée si toVersion est supérieur à la valeur actuelle de la propriété Version.

Dans la mesure où les événements confirmés ne changent jamais, il n’existe aucun risque de compétition, même en présence de plusieurs instances ou en cas de confirmation retardée. Toutefois, dans ce genre de situation, la valeur de la propriété Version peut être plus grande au moment de la reprise de await qu’au moment de l’appel de RetrieveConfirmedEvents. Il peut donc être judicieux d’enregistrer sa valeur dans une variable. Consultez également la section sur les garanties de concurrence.