Dela via


Kornreferenser

Innan du anropar en metod på ett korn behöver du först en referens till det kornet. En kornreferens är ett proxyobjekt som implementerar samma korngränssnitt som motsvarande kornklass. Den kapslar in målkornets logiska identitet (typ och unik nyckel). Du använder kornreferenser för att göra anrop till målintervallet. Varje kornreferens pekar på ett enda korn (en enda instans av kornklassen), men du kan skapa flera oberoende referenser till samma korn.

Eftersom en kornreferens representerar målkornets logiska identitet är den oberoende av kornets fysiska plats och förblir giltig även efter en fullständig omstart av systemet. Du kan använda kornreferenser som andra .NET-objekt. Du kan skicka den till en metod, använda den som ett metodreturvärde och till och med spara den i beständig lagring.

Du kan hämta en kornreferens 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 kornet inom dess typ.

I följande exempel visas hur du hämtar en kornreferens för gränssnittet IPlayerGrain som definierats tidigare.

Inifrån en kornklass:

// This would typically be read from an HTTP request parameter or elsewhere.
Guid playerId = Guid.NewGuid();
IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);

Från Orleans klientkod:

// This would typically be read from an HTTP request parameter or elsewhere.
Guid playerId = Guid.NewGuid();
IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);

Kornreferenser innehåller tre informationsdelar:

  1. Korntypen, som unikt identifierar kornklassen.
  2. Kornnyckeln, som unikt identifierar en logisk instans av den korniga klassen.
  3. Gränssnittet som kornreferensen måste implementera.

Anmärkning

Korntypen och nyckeln utgör kornidentiteten.

Observera att föregående anrop till IGrainFactory.GetGrain endast accepterade två av dessa tre saker:

  • Gränssnittet som implementeras av kornreferensen, IPlayerGrain.
  • Grainnyckeln, vilket är värdet av playerId.

Trots att det anges att en kornreferens innehåller en korntyp, nyckel och gränssnitt, tillhandahålls Orleans exemplen endast med nyckeln och gränssnittet. Detta beror på att Orleans underhåller en mappning mellan korngränssnitt och korntyper. När du ber kornfabriken om IShoppingCartGrain, konsulterar Orleans dess mappning för att hitta motsvarande korntyp så att den kan skapa referensen. Detta fungerar när det bara finns en implementering av ett kornigt gränssnitt. Men om det finns flera implementeringar måste du skilja dem åt i anropet GetGrain . För mer information, se nästa avsnitt, Upplösning av korntypens disambiguering.

Anmärkning

Orleans genererar implementeringstyper för kornreferenser för varje korngränssnitt i ditt program under kompileringen. Dessa referensimplementeringar för korn ärver från Orleans.Runtime.GrainReference klassen. GetGrain returnerar instanser av den genererade Orleans.Runtime.GrainReference implementeringen som motsvarar det begärda korngränssnittet.

Tvetydig upplösning av korntyp

När det finns flera implementeringar av ett korngränssnitt, till exempel i följande exempel, Orleans försöker fastställa den avsedda implementeringen när du skapar en kornreferens. Tänk på följande exempel, där det finns två implementeringar av ICounterGrain gränssnittet:

public interface ICounterGrain : IGrainWithStringKey
{
    ValueTask<int> UpdateCount();
}

public class UpCounterGrain : ICounterGrain
{
    private int _count;

    public ValueTask<string> UpdateCount() => new(++_count); // Increment count
}

public class DownCounterGrain : ICounterGrain
{
    private int _count;

    public ValueTask<string> UpdateCount() => new(--_count); // Decrement count
}

Följande anrop till GetGrain genererar ett undantag eftersom Orleans inte vet hur man entydigt ska mappa ICounterGrain till någon av de möjliga klassarna.

// This will throw an exception: there is no unambiguous mapping from ICounterGrain to a grain class.
ICounterGrain myCounter = grainFactory.GetGrain<ICounterGrain>("my-counter");

Ett System.ArgumentException utlöses med följande meddelande:

Unable to identify a single appropriate grain type for interface ICounterGrain. Candidates: upcounter (UpCounterGrain), downcounter (DownCounterGrain)

Felmeddelandet anger vilka kornimplementeringar som har hittats Orleans som matchar den begärda korngränssnittstypen . ICounterGrain Den visar korntypsnamnen (upcounter och downcounter) och kornklasserna (UpCounterGrain och DownCounterGrain).

Anmärkning

Korntypsnamnen i föregående felmeddelande, upcounter och downcounter, härleds från kornklassnamnen UpCounterGrain respektive DownCounterGrain . Det här är standardbeteendet i Orleans och det kan anpassas genom att lägga till ett [GrainType(string)] attribut i kornklassen. Till exempel:

[GrainType("up")]
public class UpCounterGrain : IUpCounterGrain { /* as above */ }

Det finns flera sätt att lösa den här tvetydigheten, som beskrivs i följande underavsnitt.

Att klargöra korntyper genom användning av unika markörersnitt

Det tydligaste sättet att skilja dessa korn är att ge dem unika korngränssnitt. Om du till exempel lägger till IUpCounterGrain gränssnittet UpCounterGrain i klassen och lägger till IDownCounterGrain gränssnittet DownCounterGrain i klassen, som i följande exempel, kan du lösa rätt kornreferens genom att skicka IUpCounterGrain eller IDownCounterGrain till anropet GetGrain<T> i stället för den tvetydiga ICounterGrain typen.

public interface ICounterGrain : IGrainWithStringKey
{
    ValueTask<int> UpdateCount();
}

// Define unique interfaces for our implementations
public interface IUpCounterGrain : ICounterGrain, IGrainWithStringKey {}
public interface IDownCounterGrain : ICounterGrain, IGrainWithStringKey {}

public class UpCounterGrain : IUpCounterGrain
{
    private int _count;

    public ValueTask<string> UpdateCount() => new(++_count); // Increment count
}

public class DownCounterGrain : IDownCounterGrain
{
    private int _count;

    public ValueTask<string> UpdateCount() => new(--_count); // Decrement count
}

Om du vill skapa en referens till något av kornen bör du överväga följande kod:

// Get a reference to an UpCounterGrain.
ICounterGrain myUpCounter = grainFactory.GetGrain<IUpCounterGrain>("my-counter");

// Get a reference to a DownCounterGrain.
ICounterGrain myDownCounter = grainFactory.GetGrain<IDownCounterGrain>("my-counter");

Anmärkning

I föregående exempel skapade du två kornreferenser med samma nyckel men olika korntyper. Den första, som lagras i variabeln myUpCounter , refererar till kornet med ID:t upcounter/my-counter. Den andra, som lagras i variabeln myDownCounter , refererar till kornet med ID:t downcounter/my-counter. Kombinationen av korntyp och kornnyckel identifierar unikt ett korn. Därför syftar myUpCounter och myDownCounter på olika korn.

Tydliggöra korntyper genom att tillhandahålla ett klassprefix för korn

Du kan ange ett prefix till ett kornklassnamn för IGrainFactory.GetGrain, som till exempel:

ICounterGrain myUpCounter = grainFactory.GetGrain<ICounterGrain>("my-counter", grainClassNamePrefix: "Up");
ICounterGrain myDownCounter = grainFactory.GetGrain<ICounterGrain>("my-counter", grainClassNamePrefix: "Down");

Ange standardimplementeringen för kornighet med hjälp av namngivningskonventionen

När du löser tvetydigheter mellan flera implementationer av samma gränssnitt Orleans väljer du en implementation genom att använda konventionen att ta bort ett inledande "I" från gränssnittsnamnet. Om till exempel gränssnittsnamnet är ICounterGrain och det finns två implementeringar, CounterGrain och DownCounterGrain, Orleans väljer när du tillfrågas CounterGrain om en referens till ICounterGrain, som i följande exempel:

/// This will refer to an instance of CounterGrain, since that matches the convention.
ICounterGrain myUpCounter = grainFactory.GetGrain<ICounterGrain>("my-counter");

Ange standardtyp för kornighet med hjälp av ett attribut

Du kan lägga till Orleans.Metadata.DefaultGrainTypeAttribute attributet i ett korngränssnitt för att ange korntyp för standardimplementeringen för gränssnittet, som du ser i följande exempel:

[DefaultGrainType("up-counter")]
public interface ICounterGrain : IGrainWithStringKey
{
    ValueTask<int> UpdateCount();
}

[GrainType("up-counter")]
public class UpCounterGrain : ICounterGrain
{
    private int _count;

    public ValueTask<string> UpdateCount() => new(++_count); // Increment count
}

[GrainType("down-counter")]
public class DownCounterGrain : ICounterGrain
{
    private int _count;

    public ValueTask<string> UpdateCount() => new(--_count); // Decrement count
}
/// This will refer to an instance of UpCounterGrain, due to the [DefaultGrainType("up-counter"')] attribute
ICounterGrain myUpCounter = grainFactory.GetGrain<ICounterGrain>("my-counter");

Avidentifiering av korntyper genom att ange det entydiga korn-ID:t

Vissa överlagringar av IGrainFactory.GetGrain accepterar ett argument av typen Orleans.Runtime.GrainId. När du använder dessa överlagringar Orleans behöver du inte mappa från en gränssnittstyp till en korntyp, så det finns ingen tvetydighet att lösa. Till exempel:

public interface ICounterGrain : IGrainWithStringKey
{
    ValueTask<int> UpdateCount();
}

[GrainType("up-counter")]
public class UpCounterGrain : ICounterGrain
{
    private int _count;

    public ValueTask<string> UpdateCount() => new(++_count); // Increment count
}

[GrainType("down-counter")]
public class DownCounterGrain : ICounterGrain
{
    private int _count;

    public ValueTask<string> UpdateCount() => new(--_count); // Decrement count
}
// This will refer to an instance of UpCounterGrain, since "up-counter" was specified as the grain type
// and the UpCounterGrain uses [GrainType("up-counter")] to specify its grain type.
ICounterGrain myUpCounter = grainFactory.GetGrain<ICounterGrain>(GrainId.Create("up-counter", "my-counter"));