Dela via


Utveckla ett korn

Innan du skriver kod för att implementera en kornig klass skapar du ett nytt klassbiblioteksprojekt som riktar sig till .NET Standard eller .Net Core (föredras) eller .NET Framework 4.6.1 eller senare (om du inte kan använda .NET Standard eller .NET Core på grund av beroenden). Korngränssnitt och kornklasser kan definieras i samma klassbiblioteksprojekt, eller i två olika projekt för bättre separation av gränssnitt från implementering. I båda fallen måste projekten referera till Microsoft.Orleans.Core.Abstractions och Microsoft.Orleans.CodeGenerator.MSBuild NuGet-paket.

Mer detaljerade instruktioner finns i avsnittet Project Installation iTutorial One – Orleans Basics.

Korniga gränssnitt och klasser

Korn interagerar med varandra och anropas utifrån genom att anropa metoder som deklarerats som en del av respektive korngränssnitt. En kornklass implementerar ett eller flera tidigare deklarerade korniga gränssnitt. Alla metoder i ett korngränssnitt måste returnera en Task (för void metoder), en Task<TResult> eller en ValueTask<TResult> (för metoder som returnerar värden av typen T).

Följande är ett utdrag från Exemplet på Orleans-närvarotjänst version 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;
   }
}

Returnera värden från kornmetoder

En kornmetod som returnerar ett värde av typen T definieras i ett korngränssnitt som returnerar en Task<T>. För kornmetoder som inte har markerats med nyckelordet async returneras det vanligtvis via följande instruktion när returvärdet är tillgängligt:

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

En kornig metod som inte returnerar något värde, i praktiken en void-metod, definieras i ett korngränssnitt som returnerar en Task. Den returnerade Task anger asynkron körning och slutförande av metoden. När en "void"-metod slutför körningen för kornmetoder som inte har markerats med nyckelordet async måste den returnera specialvärdet Task.CompletedTask:

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

En kornig metod markerad som async returnerar värdet direkt:

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

En void kornig metod markerad som async som returnerar inget värde som helt enkelt returneras i slutet av körningen:

public async Task GrainMethod4()
{
    return;
}

Om en kornig metod tar emot returvärdet från ett annat asynkront metodanrop, till ett korn eller inte, och inte behöver utföra felhantering av det anropet, kan den helt enkelt returnera det som det tar emot från det asynkrona anropet Task :

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

    return task;
}

På samma sätt kan en void kornmetod returnera en som returneras till den av ett Task annat anrop i stället för att vänta på den.

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

ValueTask<T> kan användas i stället för Task<T>.

Kornreferens

En kornreferens är ett proxyobjekt som implementerar samma korniga gränssnitt som motsvarande kornklass. Den kapslar in målkornets logiska identitet (typ och unik nyckel). En kornreferens används för att göra anrop till målintervallet. Varje kornreferens är till ett enda korn (en enskild instans av kornklassen), men en kan skapa flera oberoende referenser till samma kornighet.

Eftersom en kornighetsreferens representerar målkornets logiska identitet är den oberoende av kornets fysiska plats och förblir giltig även efter en fullständig omstart av systemet. Utvecklare kan använda kornighetsreferenser som andra .NET-objekt. Den kan skickas till en metod, användas som ett metodreturvärde osv. och till och med sparas i beständig lagring.

En kornreferens kan hämtas genom att skicka identiteten för ett korn till IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) metoden, där T är korngränssnittet och key är den unika nyckeln för kornigheten inom typen.

Följande är exempel på hur du hämtar en kornreferens för gränssnittet IPlayerGrain som definierats ovan.

Inifrån en kornklass:

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

Från Orleans-klientkod.

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

Anrop av kornmetod

Orleans-programmeringsmodellen baseras på asynkron programmering. Med hjälp av kornreferensen från föregående exempel gör du så här för att utföra ett anrop av kornmetod:

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

Det går att ansluta till två eller flera Tasks. Kopplingsåtgärden skapar en ny Task som löses när alla dess komponenter Taskhar slutförts. Det här är ett användbart mönster när ett korn måste starta flera beräkningar och vänta tills alla har slutförts innan du fortsätter. Ett frontend-korn som genererar en webbsida som består av många delar kan till exempel göra flera backend-anrop, ett för varje del, och få ett Task för varje resultat. Kornigheten väntar sedan på att alla dessa Tasksska kopplas. När kopplingen Task har lösts har de enskilda Taskfilerna slutförts och alla data som krävs för att formatera webbsidan har tagits emot.

Exempel:

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.

Virtuella metoder

En kornklass kan också åsidosätta OnActivateAsync och OnDeactivateAsync virtuella metoder. Dessa anropas av Orleans-körningen vid aktivering och inaktivering av varje korn i klassen. Detta ger kornkoden en chans att utföra ytterligare initierings- och rensningsåtgärder. Ett undantag som genereras av OnActivateAsync misslyckas aktiveringsprocessen. Även OnActivateAsyncom , om det åsidosätts, alltid anropas som en del av kornaktiveringsprocessen, OnDeactivateAsync är det inte garanterat att anropas i alla situationer, till exempel i händelse av ett serverfel eller annan onormal händelse. Därför bör program inte förlita sig på OnDeactivateAsync för att utföra kritiska åtgärder, till exempel beständighet av tillståndsändringar. De bör endast använda den för åtgärder med bästa förmåga.