Delen via


Een graan ontwikkelen

Voordat u code schrijft om een graanklasse te implementeren, maakt u een nieuw klassebibliotheekproject dat is gericht op .NET Standard of .Net Core (voorkeur) of .NET Framework 4.6.1 of hoger (als u .NET Standard of .NET Core niet kunt gebruiken vanwege afhankelijkheden). Graaninterfaces en graanklassen kunnen worden gedefinieerd in hetzelfde klassebibliotheekproject of in twee verschillende projecten voor een betere scheiding van interfaces van implementatie. In beide gevallen moeten de projecten verwijzen naar Microsoft.Orleans.Core.Abstractions en Microsoft.Orleans.CodeGenerator.MSBuild NuGet-pakketten.

Zie de sectie Project Setup van Tutorial One – Orleans Basics voor meer gedetailleerde instructies.

Graaninterfaces en -klassen

Korrels communiceren met elkaar en worden van buitenaf aangeroepen door methoden aan te roepen die zijn gedeclareerd als onderdeel van de respectieve graaninterfaces. Een graanklasse implementeert een of meer eerder gedeclareerde graaninterfaces. Alle methoden van een graaninterface moeten een Task (voor void methoden), een Task<TResult> of a ValueTask<TResult> retourneren (voor methoden die waarden van het type Tretourneren).

Hier volgt een fragment uit het voorbeeld van de Orleans versie 1.5 Presence Service:

public interface IPlayerGrain : IGrainWithGuidKey
{
    Task<IGameGrain> GetCurrentGame();
    Task JoinGame(IGameGrain game);
    Task LeaveGame(IGameGrain game);
}

public class PlayerGrain : Grain, IPlayerGrain
{
    private IGameGrain _currentGame;

    // Game the player is currently in. May be null.
    public Task<IGameGrain> GetCurrentGame()
    {
       return Task.FromResult(_currentGame);
    }

    // Game grain calls this method to notify that the player has joined the game.
    public Task JoinGame(IGameGrain game)
    {
       _currentGame = game;

       Console.WriteLine(
           $"Player {GetPrimaryKey()} joined game {game.GetPrimaryKey()}");

       return Task.CompletedTask;
    }

   // Game grain calls this method to notify that the player has left the game.
   public Task LeaveGame(IGameGrain game)
   {
       _currentGame = null;

       Console.WriteLine(
           $"Player {GetPrimaryKey()} left game {game.GetPrimaryKey()}");

       return Task.CompletedTask;
   }
}

Retourwaarden van graanmethoden

Een korrelmethode die een waarde van het type T retourneert, wordt gedefinieerd in een graaninterface als het retourneren van een Task<T>. Voor graanmethoden die niet zijn gemarkeerd met het async trefwoord, wanneer de retourwaarde beschikbaar is, wordt deze meestal geretourneerd via de volgende instructie:

public Task<SomeType> GrainMethod1()
{
    return Task.FromResult(GetSomeType());
}

Een korrelmethode die geen waarde retourneert, effectief een ongeldige methode, wordt gedefinieerd in een graaninterface als het retourneren van een Task. Het geretourneerde Task geeft asynchrone uitvoering en voltooiing van de methode aan. Voor graanmethoden die niet zijn gemarkeerd met het async trefwoord, moet een 'void'-methode de uitvoering ervan voltooien, de speciale waarde van Task.CompletedTask:

public Task GrainMethod2()
{
    return Task.CompletedTask;
}

Een korrelmethode die is gemarkeerd als async retourneert de waarde rechtstreeks:

public async Task<SomeType> GrainMethod3()
{
    return await GetSomeTypeAsync();
}

Een void korrelmethode die is gemarkeerd als async die retourneert dat er geen waarde wordt geretourneerd aan het einde van de uitvoering:

public async Task GrainMethod4()
{
    return;
}

Als een korrelmethode de retourwaarde ontvangt van een andere asynchrone methode-aanroep, naar een korrel of niet, en de foutafhandeling van die aanroep niet hoeft uit te voeren, kan deze gewoon het Task ontvangen van die asynchrone aanroep retourneren:

public Task<SomeType> GrainMethod5()
{
    Task<SomeType> task = CallToAnotherGrain();

    return task;
}

Op dezelfde manier kan een void graanmethode een Task geretourneerde methode retourneren door een andere aanroep in plaats van erop te wachten.

public Task GrainMethod6()
{
    Task task = CallToAsyncAPI();
    return task;
}

ValueTask<T> kan worden gebruikt in plaats van Task<T>.

Verwijzing naar korrels

Een grainverwijzing is een proxyobject dat dezelfde graaninterface implementeert als de bijbehorende graanklasse. De logische identiteit (type en unieke sleutel) van het doelkorrel wordt ingekapseld. Een korrelreferentie wordt gebruikt voor het aanroepen van het doelkorrel. Elke korrelverwijzing is naar één korrel (één exemplaar van de graanklasse), maar men kan meerdere onafhankelijke verwijzingen naar hetzelfde graan maken.

Aangezien een korrelreferentie de logische identiteit van het doelkorrel vertegenwoordigt, is deze onafhankelijk van de fysieke locatie van het graan en blijft deze geldig, zelfs na een volledige herstart van het systeem. Ontwikkelaars kunnen korrelverwijzingen gebruiken zoals elk ander .NET-object. Deze kan worden doorgegeven aan een methode, gebruikt als retourwaarde voor de methode, enzovoort, en kan zelfs worden opgeslagen in permanente opslag.

Een graanreferentie kan worden verkregen door de identiteit van een graan door te geven aan de IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) methode, waarbij T de graaninterface en key de unieke sleutel van het graan binnen het type is.

Hier volgen enkele voorbeelden van het verkrijgen van een korrelreferentie van de IPlayerGrain hierboven gedefinieerde interface.

Van binnen een graanklasse:

IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);

Vanuit Orleans-clientcode.

IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);

Aanroep van korrelmethode

Het Orleans-programmeermodel is gebaseerd op asynchrone programmering. Met behulp van de korrelreferentie uit het vorige voorbeeld kunt u als volgt een aanroep van een korrelmethode uitvoeren:

// Invoking a grain method asynchronously
Task joinGameTask = player.JoinGame(this);

// The await keyword effectively makes the remainder of the
// method execute asynchronously at a later point
// (upon completion of the Task being awaited) without blocking the thread.
await joinGameTask;

// The next line will execute later, after joinGameTask has completed.
players.Add(playerId);

Het is mogelijk om twee of meer Taskssamen te voegen; de joinbewerking maakt een nieuwe Task die wordt opgelost wanneer alle samenstellende Taskonderdelen zijn voltooid. Dit is een nuttig patroon wanneer een korrel meerdere berekeningen moet starten en moet wachten totdat alle berekeningen zijn voltooid voordat u doorgaat. Een front-endgranen die een webpagina van veel onderdelen genereert, kunnen bijvoorbeeld meerdere back-endaanroepen maken, één voor elk onderdeel en een Task voor elk resultaat ontvangen. Het graan wacht vervolgens op de join van al deze Tasks; wanneer de join Task is opgelost, zijn de afzonderlijke Taskexemplaren voltooid en alle gegevens die nodig zijn om de webpagina op te maken, ontvangen.

Voorbeeld:

List<Task> tasks = new List<Task>();
Message notification = CreateNewMessage(text);

foreach (ISubscriber subscriber in subscribers)
{
    tasks.Add(subscriber.Notify(notification));
}

// WhenAll joins a collection of tasks, and returns a joined
// Task that will be resolved when all of the individual notification Tasks are resolved.
Task joinedTask = Task.WhenAll(tasks);

await joinedTask;

// Execution of the rest of the method will continue
// asynchronously after joinedTask is resolve.

Virtuele methoden

Een graanklasse kan eventueel overschrijven OnActivateAsync en OnDeactivateAsync virtuele methoden; deze worden aangeroepen door de Orleans-runtime bij activering en deactivering van elk graan van de klasse. Dit geeft de graancode de kans om extra initialisatie- en opschoonbewerkingen uit te voeren. Een uitzondering die wordt gegenereerd door OnActivateAsync het activeringsproces mislukt. Hoewel OnActivateAsync, indien overschreven, altijd wordt aangeroepen als onderdeel van het korrelactiveringsproces, OnDeactivateAsync wordt niet gegarandeerd in alle situaties, bijvoorbeeld in het geval van een serverfout of een andere abnormale gebeurtenis. Daarom moeten toepassingen niet afhankelijk zijn OnDeactivateAsync van het uitvoeren van kritieke bewerkingen, zoals de persistentie van statuswijzigingen. Ze moeten deze alleen gebruiken voor best effort-bewerkingen.