Dela via


Grunderna i JournaledGrain

Journalfördelade korn härleds från JournaledGrain<TGrainState,TEventBase>, med följande typparametrar:

  • TGrainState Representerar kornets tillstånd. Det måste vara en klass med en offentlig standardkonstruktor.
  • TEventBase är en vanlig supertyp för alla händelser som kan aktiveras för det här kornet och kan vara vilken klass eller vilket gränssnitt som helst.

Alla tillstånds- och händelseobjekt ska vara serialiserbara (eftersom loggkonsekvensprovidrar kan behöva bevara dem och/eller skicka dem i meddelanden).

För korn vars händelser är POCO:er (vanliga gamla C#-objekt) JournaledGrain<TGrainState> kan användas som en förkortning för JournaledGrain<TGrainState,TEventBase>.

Läsa korntillståndet

Om du vill läsa det aktuella korntillståndet och fastställa dess versionsnummer har JournaledGrain egenskaper

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

Versionsnumret är alltid lika med det totala antalet bekräftade händelser, och tillståndet är resultatet av att tillämpa alla bekräftade händelser på det ursprungliga tillståndet. Det ursprungliga tillståndet, som har version 0 (eftersom inga händelser har tillämpats på det), bestäms av standardkonstruktorn för klassen GrainState.

Viktigt: Programmet bör aldrig direkt ändra objektet som returneras av State. Det är endast avsett för läsning. När programmet vill ändra tillståndet måste det snarare göra det indirekt genom att skapa händelser.

Skapa händelser

Att höja händelser utförs genom att anropa RaiseEvent funktionen. Ett korn som representerar en chatt kan till exempel generera ett PostedEvent för att indikera att en användare skickade ett inlägg:

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

Observera att RaiseEvent startar en skrivning till lagringsåtkomst, men väntar inte på att skrivningen ska slutföras. För många program är det viktigt att vänta tills vi har bekräftat att händelsen har sparats. I så fall följer vi alltid upp genom att vänta ConfirmEventspå :

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

Observera att även om du inte uttryckligen anropar ConfirmEventskommer händelserna så småningom att bekräftas – det sker automatiskt i bakgrunden.

Tillståndsövergångsmetoder

Körningen uppdaterar korntillståndet automatiskt när händelser aktiveras. Programmet behöver inte uttryckligen uppdatera tillståndet efter att en händelse har skapats. Programmet måste dock fortfarande ange den kod som anger hur tillståndet ska uppdateras som svar på en händelse. Detta kan göras på två sätt.

(a) Klassen GrainState kan implementera en eller flera Apply metoder på StateType. Vanligtvis skapar man flera överlagringar och den närmaste matchningen väljs för körningstypen för händelsen:

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

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

(b) Kornet kan åsidosätta TransitionState funktionen:

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

Övergångsmetoderna antas inte ha några andra biverkningar än att ändra tillståndsobjektet och bör vara deterministiska (annars är effekterna oförutsägbara). Om övergångskoden utlöser ett undantag fångas undantaget upp och inkluderas i en varning i loggen Orleans som utfärdas av loggkonsekvensprovidern.

Exakt när körningen anropar övergångsmetoderna beror på den valda loggkonsekvensprovidern och dess konfiguration. Program bör inte förlita sig på en viss tidpunkt, förutom när det är specifikt garanterat av loggkonsekvensprovidern.

Vissa leverantörer, till exempel Orleans.EventSourcing.LogStorage loggkonsekvensprovidern, spelar upp händelsesekvensen varje gång kornet läses in. Så länge händelseobjekten fortfarande kan deserialiseras från lagringen är det därför möjligt att radikalt ändra GrainState klassen och övergångsmetoderna. Men för andra leverantörer, till exempel Orleans.EventSourcing.StateStorage loggkonsekvensprovidern, sparas bara GrainState objektet, så utvecklare måste se till att det kan deserialiseras korrekt när de läse från lagringen.

Skapa flera händelser

Du kan göra flera anrop till RaiseEvent innan du anropar ConfirmEvents:

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

Detta kommer dock sannolikt att orsaka två på varandra följande lagringsåtkomster, och det medför en risk att kornet misslyckas när du bara har skrivit den första händelsen. Därför är det oftast bättre att skapa flera händelser samtidigt med hjälp av

RaiseEvents(IEnumerable<EventType> events)

Detta garanterar att den angivna sekvensen av händelser skrivs till lagring atomiskt. Observera att eftersom versionsnumret alltid matchar längden på händelsesekvensen ökar versionsnumret med fler än en i taget om flera händelser höjs.

Hämta händelsesekvensen

Med följande metod från basklassen JournaledGrain kan programmet hämta ett angivet segment av sekvensen för alla bekräftade händelser:

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

Det stöds dock inte av alla loggkonsekvensproviders. Om det inte stöds, eller om det angivna segmentet i sekvensen inte längre är tillgängligt, genereras en NotSupportedException .

Om du vill hämta alla händelser till den senaste bekräftade versionen anropar man

await RetrieveConfirmedEvents(0, Version);

Endast bekräftade händelser kan hämtas: ett undantag utlöses om toVersion är större än det aktuella värdet för egenskapen Version.

Eftersom bekräftade händelser aldrig ändras finns det inga raser att oroa sig för, inte ens i närvaro av flera instanser eller fördröjd bekräftelse. I sådana situationer kan värdet för egenskapen Version dock vara större när await återupptas än vid den tidpunkt RetrieveConfirmedEvents då anropas, så det kan vara lämpligt att spara värdet i en variabel. Se även avsnittet om samtidighetsgarantier.