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:
- Graintyp, der die Grainklasse eindeutig identifiziert
- Grainschlüssel, der eine logische Instanz dieser Grainklasse eindeutig identifiziert
- 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"));