Leggere in inglese

Condividi tramite


Sviluppare una grana

Prima di scrivere codice per implementare una classe grain, creare un nuovo progetto libreria di classi con destinazione .NET Standard o .NET Core (preferito) o .NET Framework 4.6.1 o versione successiva (se non è possibile usare .NET Standard o .NET Core a causa delle dipendenze). Le interfacce grain e le classi grain possono essere definite nello stesso progetto libreria di classi o in due progetti diversi per una migliore separazione delle interfacce dall'implementazione. In entrambi i casi, i progetti devono fare riferimento ai pacchetti Microsoft.Orleans.Core.Abstractions e Microsoft.Orleans.CodeGenerator.MSBuild NuGet.

Per istruzioni più dettagliate, vedere la Project configurazione diTutorial One - Orleans Basics.

Interfacce e classi grain

I grani interagiscono tra loro e vengono chiamati dall'esterno richiamando i metodi dichiarati come parte delle rispettive interfacce grain. Una classe grain implementa una o più interfacce grain dichiarate in precedenza. Tutti i metodi di un'interfaccia grain devono restituire un (per void i metodi), un o Task<TResult>ValueTask<TResult> un (per i metodi che restituiscono valori di tipo T).Task

Di seguito è riportato un estratto dell'esempio servizio Presence di Orleans versione 1.5:

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;
   }
}

Restituire valori dai metodi di granulosità

Un metodo grain che restituisce un valore di tipo T viene definito in un'interfaccia grain come che restituisce un oggetto Task<T>. Per i metodi grain non contrassegnati con la async parola chiave , quando il valore restituito è disponibile, viene in genere restituito tramite l'istruzione seguente:

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

Un metodo grain che non restituisce alcun valore, in effetti un metodo void, viene definito in un'interfaccia grain come che restituisce un oggetto Task. L'oggetto Task restituito indica l'esecuzione asincrona e il completamento del metodo. Per i metodi grain non contrassegnati con la async parola chiave , quando un metodo "void" completa l'esecuzione, deve restituire il valore speciale di Task.CompletedTask:

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

Un metodo grain contrassegnato come async restituisce direttamente il valore:

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

Un void metodo grain contrassegnato come async che non restituisce alcun valore restituisce semplicemente alla fine dell'esecuzione:

public async Task GrainMethod4()
{
    return;
}

Se un metodo grain riceve il valore restituito da un'altra chiamata di metodo asincrona a una granulosità o meno e non deve eseguire la gestione degli errori di tale chiamata, Task può semplicemente restituire il valore ricevuto da tale chiamata asincrona:

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

    return task;
}

Analogamente, un metodo grain void può restituire un oggetto Task restituito da un'altra chiamata anziché attenderlo.

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

ValueTask<T> può essere usato al posto di Task<T>.

Informazioni di riferimento sulla granularità

Un riferimento grain è un oggetto proxy che implementa la stessa interfaccia grain della classe grain corrispondente. Incapsula l'identità logica (tipo e chiave univoca) del grano di destinazione. Un riferimento granulosità viene usato per effettuare chiamate alla granulosità di destinazione. Ogni riferimento granulosità è a una singola granulosità (una singola istanza della classe grain), ma è possibile creare più riferimenti indipendenti alla stessa granulosità.

Poiché un riferimento granulosità rappresenta l'identità logica della granulosità di destinazione, è indipendente dalla posizione fisica della granulosità e rimane valido anche dopo un riavvio completo del sistema. Gli sviluppatori possono usare riferimenti granulari come qualsiasi altro oggetto .NET. Può essere passato a un metodo, usato come valore restituito del metodo e così via, e anche salvato nell'archiviazione permanente.

È possibile ottenere IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) un riferimento alla granulosità passando l'identità di una grana al metodo , Tkey dove è l'interfaccia grain e è la chiave univoca della grana all'interno del tipo.

Di seguito sono riportati esempi di come ottenere un riferimento granulare dell'interfaccia IPlayerGrain definita in precedenza.

Dall'interno di una classe grain:

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

Dal codice client di Orleans.

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

Chiamata al metodo Grain

Il modello di programmazione Orleans è basato sulla programmazione asincrona. Usando il riferimento alla granulosità dell'esempio precedente, ecco come eseguire una chiamata al metodo grain:

// 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);

È possibile creare un join di due Taskso più elementi . L'operazione di join TaskTaskcrea un nuovo elemento che viene risolto al completamento di tutti i relativi componenti. Si tratta di un modello utile quando una granularità deve avviare più calcoli e attendere il completamento di tutti prima di procedere. Ad esempio, una grana front-end che genera una pagina Web fatta di molte parti potrebbe effettuare più chiamate back-end, una per ogni parte, e ricevere un Task per ogni risultato. Il grano attende quindi il join Tasksdi tutti questi elementi. Quando il join Task viene risolto, Taski singoli s sono stati completati e tutti i dati necessari per formattare la pagina Web sono stati ricevuti.

Esempio:

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.

Metodi virtuali

Una classe grain può facoltativamente eseguire l'override OnActivateAsyncOnDeactivateAsync dei metodi virtuali e , che vengono richiamati dal runtime di Orleans all'attivazione e alla disattivazione di ogni granulo della classe. In questo modo il codice granulare ha la possibilità di eseguire operazioni di inizializzazione e pulizia aggiuntive. Un'eccezione generata da non riesce OnActivateAsync nel processo di attivazione. Anche OnActivateAsyncse , se sottoposto a override, viene sempre chiamato come parte del processo di attivazione granulare, OnDeactivateAsync non è garantito che venga chiamato in tutte le situazioni, ad esempio in caso di errore del server o di un altro evento anomalo. Di conseguenza, le applicazioni non devono basarsi su OnDeactivateAsync per eseguire operazioni critiche, ad esempio la persistenza delle modifiche dello stato. È consigliabile usarlo solo per operazioni ottimali.