Freigeben über


Grainverweise

Bevor Sie eine Methode für Grains aufrufen, benötigen Sie zuerst einen Verweis auf dieses Grain. Ein Grainverweis ist ein Proxyobjekt, das dieselbe Grainschnittstelle wie die entsprechende Grainklasse implementiert. Er kapselt die logische Identität (Typ und eindeutiger Schlüssel) des Ziel-Grains ein. Grainverweise werden verwendet, um Aufrufe an das Zielgrain zu tätigen. Jeder Grain-Verweis ist auf ein einzelnes Grain (eine einzelne Instanz der Grain-Klasse), aber man kann mehrere unabhängige Verweise auf dasselbe Grain erstellen.

Da ein Grain-Verweis die logische Identität des Ziel-Grains darstellt, ist er unabhängig von der physischen Position des Grains und bleibt auch nach einem vollständigen Neustart des Systems gültig. Entwickler können Grain-Verweise wie jedes andere .NET-Objekt verwenden. Sie können an eine Methode übergeben, als Methodenrückgabewert verwendet und sogar im persistenten Speicher gespeichert werden.

Ein Grain-Verweis kann abgerufen werden, indem die Identität eines Grains an die IGrainFactory.GetGrain<TGrainInterface>(Type, Guid)-Methode übergeben wird, wobei T die Grain-Schnittstelle und key der eindeutige Schlüssel des Grains innerhalb des Typs ist.

Im Folgenden finden Sie Beispiele zum Abrufen eines Grain-Verweises der oben definierten IPlayerGrain-Schnittstelle.

In einer Grainklasse:

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

Über Orleans-Clientcode:

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

Grainverweise enthalten drei wichtige Informationen:

  1. Graintyp, der die Grainklasse eindeutig identifiziert
  2. Grainschlüssel, der eine logische Instanz dieser Grainklasse eindeutig identifiziert
  3. Schnittstelle, die vom Grainverweis implementiert werden muss

Hinweis

Der Graintyp und der Grainschlüssel bilden die Grainidentität.

Beachten Sie, dass die oben genannten Aufrufe von IGrainFactory.GetGrain nur zwei dieser drei Elemente akzeptiert haben:

  • Die Schnittstelle, die durch den Grainverweis IPlayerGrain implementiert wird
  • Den Grainschlüssel mit dem Wert playerId

Trotz der Aussage, dass ein Grainverweis einen Graintyp, einen Grainschlüssel und eine Schnittstelle enthält, haben die Beispiele nur Orleans mit dem Schlüssel und der Schnittstelle angegeben. Das liegt daran, dass Orleans eine Zuordnung zwischen Grainschnittstellen und Graintypen beibehält. Wenn Sie die Grainfactory nach IShoppingCartGrain abfragen, konsultiert Orleans die Zuordnung, um den entsprechenden Graintyp zu ermitteln, damit der Verweis erstellt werden kann. Dies funktioniert, wenn nur eine Implementierung einer Grainschnittstelle vorhanden ist. Wenn jedoch mehrere Implementierungen vorhanden sind, müssen Sie sie im Aufruf von GetGrain eindeutig machen. Weitere Informationen finden Sie im nächsten Abschnitt: Vermeiden von Mehrdeutigkeit bei der Graintypauflösung.

Hinweis

Orleans generiert während der Kompilierung Grainverweis-Implementierungstypen für jede Grainschnittstelle in Ihrer Anwendung. Diese Grainverweisimplementierungen erben von der Orleans.Runtime.GrainReference-Klasse. GetGrain gibt Instanzen der generierten Orleans.Runtime.GrainReference-Implementierung zurück, die der angeforderten Grainschnittstelle entspricht.

Vermeiden von Mehrdeutigkeit bei der Graintypauflösung

Wenn mehrere Implementierungen einer Grainschnittstelle vorhanden sind, z. B. im folgenden Beispiel, versucht Orleans beim Erstellen eines Grainverweises, die beabsichtigte Implementierung zu bestimmen. Betrachten Sie das folgende Beispiel, das zwei Implementierungen der ICounterGrain-Schnittstelle enthält:

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
}

Der folgende Aufruf von GetGrain löst eine Ausnahme aus, da Orleans nicht weiß, wie ICounterGrain eindeutig einer der Grainklassen zugeordnet werden kann.

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

System.ArgumentException wird mit der folgenden Meldung ausgelöst:

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

Die Fehlermeldung teilt Ihnen mit, welche Grainimplementierung Orleans enthält, die dem angeforderten Grainschnittstellentyp ICounterGrain entspricht. Sie enthält die Graintypnamen (upcounter und downcounter) sowie die Grainklassen (UpCounterGrain und DownCounterGrain).

Hinweis

Die Graintypnamen upcounter und downcounter in der vorherigen Fehlermeldung werden von den jeweiligen Grainklassennamen UpCounterGrain und DownCounterGrain abgeleitet. Dies ist das Standardverhalten in Orleans. Es kann durch Hinzufügen eines [GrainType(string)]-Attributs zur Grainklasse angepasst werden. Beispiel:

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

Es gibt mehrere Möglichkeiten, diese Mehrdeutigkeit zu beheben. Dies ist in den folgenden Unterabschnitten detailliert beschrieben.

Vermeiden der Mehrdeutigkeit von Graintypen mithilfe eindeutiger Markerschnittstellen

Die eindeutigste Methode, Mehrdeutigkeit bei diesen Grains zu vermeiden, besteht darin, ihnen eindeutige Grainschnittstellen zuzuordnen. Wenn wir beispielsweise wie im folgenden Beispiel der Klasse UpCounterGrain die Schnittstelle IUpCounterGrain und der Klasse DownCounterGrain die Schnittstelle IDownCounterGrain hinzufügen, können wir den richtigen Grainverweis auflösen, indem wir IUpCounterGrain oder IDownCounterGrain an den Aufruf GetGrain<T> übergeben, anstatt den mehrdeutigen Typ ICounterGrain zu übergeben.

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
}

Wenn Sie einen Verweis auf ein Grain erstellen möchten, sollten Sie den folgenden Code berücksichtigen:

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

Hinweis

Im vorherigen Beispiel haben Sie zwei Grainverweise mit demselben Schlüssel, aber unterschiedliche Graintypen erstellt. Der erste, der in der Variablen myUpCounter gespeichert ist, ist ein Verweis auf das Grain mit der ID upcounter/my-counter. Der zweite, der in der Variablen myDownCounter gespeichert ist, ist ein Verweis auf das Grain mit der ID downcounter/my-counter. Es ist die Kombination aus Graintyp und Grainschlüssel, die ein Grain eindeutig identifiziert. Daher verweisen myUpCounter und myDownCounter auf unterschiedliche Grains.

Vermeiden der Mehrdeutigkeit von Graintypen durch Bereitstellen eines Grainklassenpräfixes

Sie können für IGrainFactory.GetGrain ein Präfix für den Grainklassennamen angeben, z. B.:

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

Angeben der Standardgrainimplementierung mithilfe der Namenskonvention

Um Mehrdeutigkeit mehrerer Implementierungen derselben Grainschnittstelle zu vermeiden, wählt Orleans eine Implementierung aus, wobei ein vorangestelltes „I“ aus dem Schnittstellennamen entfernt wird. Wenn der Schnittstellenname beispielsweise ICounterGrain lautet und zwei Implementierungen (CounterGrain und DownCounterGrain) vorhanden sind, wählt Orleans bei der Aufforderung nach einem Verweis auf ICounterGrain CounterGrain aus, wie im folgenden Beispiel dargestellt:

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

Angeben des Standardgraintyps mithilfe eines Attributs

Das Orleans.Metadata.DefaultGrainTypeAttribute-Attribut kann einer Grainschnittstelle hinzugefügt werden, um den Graintyp der Standardimplementierung für diese Schnittstelle anzugeben, wie im folgenden Beispiel gezeigt:

[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");

Vermeiden der Mehrdeutigkeit von Graintypen durch Angabe der aufgelösten Grain-ID

Einige Überladungen von IGrainFactory.GetGrain akzeptieren ein Argument vom Typ Orleans.Runtime.GrainId. Wenn Sie diese Überladungen verwenden, muss Orleans keine Zuordnung zwischen einem Schnittstellentyp und einem Graintyp durchführen. Daher muss keine Mehrdeutigkeit behoben werden. Beispiel:

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