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 T
retourneren).
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 Tasks
samen te voegen; de joinbewerking maakt een nieuwe Task
die wordt opgelost wanneer alle samenstellende Task
onderdelen 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 Task
exemplaren 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.