Opracowywanie ziarna

Przed napisaniem kodu implementowania klasy ziarna utwórz nowy projekt biblioteki klas przeznaczony dla .NET Standard lub .Net Core (preferowany) albo .NET Framework 4.6.1 lub nowszej (jeśli nie można użyć programu .NET Standard lub .NET Core z powodu zależności). Interfejsy ziarna i klasy ziarna można zdefiniować w tym samym projekcie biblioteki klas lub w dwóch różnych projektach w celu lepszego oddzielenia interfejsów od implementacji. W obu przypadkach projekty muszą odwoływać się do pakietów Microsoft.Orleans.Core.Abstractions i Microsoft.Orleans.CodeGenerator.MSBuild NuGet pakietów.

Aby uzyskać bardziej szczegółowe instrukcje, zobacz Project Setup (Samouczek 1 — podstawy orleans).

Klasy i interfejsy ziarna

Ziarna współdziałają ze sobą i są wywoływane z zewnątrz przez wywoływanie metod zadeklarowanych jako część odpowiednich interfejsów ziarna. Klasa ziarna implementuje co najmniej jeden wcześniej zadeklarowany interfejs ziarna. Wszystkie metody interfejsu ziarna muszą zwracać wartość Task (dla void metod), a Task<TResult>ValueTask<TResult> lub (dla metod zwracających wartości typu T).

Poniżej znajduje się fragment przykładu Orleans version 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;
   }
}

Zwracanie wartości z metod ziarna

Metoda ziarna, która zwraca wartość typu, jest T definiowana w interfejsie ziarna jako zwracana przez .Task<T> W przypadku metod ziarna, które nie zostały async oznaczone słowem kluczowym , gdy wartość zwracana jest dostępna, zwykle jest zwracana za pośrednictwem następującej instrukcji:

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

Metoda ziarna, która nie zwraca żadnej wartości, w praktyce metoda void, jest definiowana w interfejsie ziarna jako zwracana przez .Task Zwrócony kod Task wskazuje asynchroniczne wykonanie i ukończenie metody. W przypadku metod ziarna async , które nie zostały oznaczone słowem kluczowym , gdy metoda "void" zakończy wykonywanie, musi zwrócić specjalną wartość Task.CompletedTask:

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

Metoda ziarna oznaczona jako async zwraca wartość bezpośrednio:

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

Metoda void ziarna oznaczona jako , async która nie zwraca żadnej wartości, po prostu zwraca po zakończeniu wykonywania:

public async Task GrainMethod4()
{
    return;
}

Jeśli metoda ziarna odbiera wartość zwracaną z innego wywołania metody asynchronicznej do ziarna lub nie i nie musi wykonywać obsługi błędów tego wywołania, Task może po prostu zwrócić odbierane z tego wywołania asynchronicznego:

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

    return task;
}

Podobnie metoda grain void może zwrócić zwrócony Task do niego przez inne wywołanie, zamiast czekać na niego.

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

ValueTask<T> Można użyć zamiast Task<T>.

Odwołanie do ziarna

Odwołanie do ziarna to obiekt proxy, który implementuje ten sam interfejs ziarna co odpowiednia klasa ziarna. Hermetyzuje tożsamość logiczną (typ i unikatowy klucz) ziarna docelowego. Odwołanie do ziarna służy do tworzenia wywołań do ziarna docelowego. Każde odwołanie do ziarna dotyczy pojedynczego ziarna (pojedynczego wystąpienia klasy ziarna), ale można utworzyć wiele niezależnych odwołań do tego samego ziarna.

Ponieważ odwołanie do ziarna reprezentuje tożsamość logiczną ziarna docelowego, jest niezależne od fizycznej lokalizacji ziarna i pozostaje ważne nawet po całkowitym ponownym uruchomieniu systemu. Deweloperzy mogą używać odwołań do ziarna tak jak każdego innego obiektu .NET. Może być przekazywany do metody, używany jako wartość zwracana metody itp., a nawet zapisywany w magazynie trwałym.

Odwołanie do ziarna można IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) uzyskać, przekazując tożsamość ziarna do metody , Tkey gdzie to interfejs ziarna i jest unikatowym kluczem ziarna w obrębie typu.

Poniżej przedstawiono przykłady sposobu uzyskania odwołania do ziarna interfejsu IPlayerGrain zdefiniowanego powyżej.

Z wewnątrz klasy ziarna:

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

Z kodu klienta orleans.

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

Wywołania metody ziarna

Model programowania Orleans opiera się na programowaniu asynchronicznym. Korzystając z odwołania do ziarna z poprzedniego przykładu, możesz wykonać wywołania metody 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);

Istnieje możliwość przyłączenia co Tasksnajmniej dwóch elementów . Task Operacja sprzężenia tworzy nową, która jest rozpoznawane po zakończeniu wszystkich składników Task. Jest to przydatny wzorzec, gdy ziarno musi uruchomić wiele obliczeń i poczekać na ukończenie wszystkich z nich przed podjęciem pracy. Na przykład ziarno frontony, które generuje stronę internetową z wieloma częściami, może tworzyć wiele wywołań zakańca, po jednym dla każdej części, i odbierać element dla Task każdego wyniku. Następnie ziarno TasksTaskTaskbędzie oczekiwało na sprzężenie wszystkich tych danych. Po rozpoznanym przyłączeniu poszczególne elementy zostały ukończone, a wszystkie dane wymagane do sformatowania strony internetowej zostały odebrane.

Przykład:

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.

Metody wirtualne

Klasa ziarna może opcjonalnie OnActivateAsyncOnDeactivateAsync przesłonić metody i metody wirtualne. Są one wywoływane przez środowisko uruchomieniowe Orleans podczas aktywacji i dezaktywacji każdego ziarna klasy. Daje to kodowi ziarna możliwość wykonania dodatkowych operacji inicjowania i czyszczenia. Wyjątek zgłoszony przez program OnActivateAsync kończy się niepowodzeniem procesu aktywacji. Jeśli OnActivateAsyncelement zostanie zastąpiony, jest zawsze wywoływany w ramach procesu aktywacji ziarna, OnDeactivateAsync nie ma gwarancji, że zostanie wywołany we wszystkich sytuacjach, na przykład w przypadku awarii serwera lub innego nietypowego zdarzenia. W związku z tym aplikacje nie powinny polegać na OnDeactivateAsync wykonywaniu krytycznych operacji, takich jak trwałość zmian stanu. Należy używać go tylko w przypadku najbardziej pracowitych operacji.