Références de grain
Avant d’appeler une méthode sur un grain, vous devez d’abord avoir une référence à ce grain. Une référence de grain est un objet proxy qui implémente la même interface de grain que la classe de grain correspondante. Il encapsule l’identité logique (type et clé unique) du grain cible. Une référence de grain est utilisée pour passer des appels au grain cible. Chaque référence de grain concerne un seul grain (un seul instance de la classe de grain), mais on peut créer plusieurs références indépendantes au même grain.
Étant donné qu’une référence de grain représente l’identité logique du grain cible, elle est indépendante de l’emplacement physique du grain et reste valide même après un redémarrage complet du système. Les développeurs peuvent utiliser des références de grain comme n’importe quel autre objet .NET. Elle peut être transmise à une méthode, utilisée comme valeur de retour de méthode, etc., et même enregistrée dans un stockage persistant.
Une référence de grain peut être obtenue en passant l’identité d’un grain à la IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) méthode, où T
est l’interface de grain et key
est la clé unique du grain dans le type.
Voici des exemples illustrant comment obtenir une référence de grain de l’interface IPlayerGrain
définie ci-dessus.
À partir d’une classe de grain :
// This would typically be read from an HTTP request parameter or elsewhere.
Guid playerId = Guid.NewGuid();
IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);
À partir du code client Orleans :
// This would typically be read from an HTTP request parameter or elsewhere.
Guid playerId = Guid.NewGuid();
IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);
Les références de grain contiennent trois informations :
- Le type de grain qui identifie de façon unique la classe de grain.
- La clé de grain qui identifie de façon unique une instance logique de cette classe de grain.
- L’interface que la référence de grain doit implémenter.
Remarque
Le type de grain et la clé forment l’identité du grain.
Notez que les appels à IGrainFactory.GetGrain ci-dessus n’acceptent que deux de ces trois éléments :
- L’interface implémentée par la référence de grain,
IPlayerGrain
. - La clé de grain, qui est la valeur de
playerId
.
Bien qu’ils indiquent qu’une référence de grain contient un type de grain, une clé et une interface, les exemples ont fourni Orleans uniquement avec la clé et l’interface. Cela est dû au fait que Orleans maintient un mappage entre les interfaces de grain et les types de grain. Lorsque vous demandez IShoppingCartGrain
à la fabrique de grains, Orleans consulte son mappage pour rechercher le type de grain correspondant afin de pouvoir créer la référence. Cette opération fonctionne lorsqu’il n’existe qu’une seule implémentation d’une interface de grain, mais s’il existe plusieurs implémentations, vous devez les distinguer dans l’appel GetGrain
. Pour obtenir plus d’informations, consultez la section suivante, différenciation de la résolution d’un type de grain.
Remarque
Orleans génère des types d’implémentation de référence de grain pour chaque interface de grain dans votre application pendant la compilation. Ces implémentations de référence de grain héritent de la classe Orleans.Runtime.GrainReference. GetGrain
retourne des instances de l’implémentation Orleans.Runtime.GrainReference générée correspondant à l’interface de grain demandée.
Différenciation de la résolution d’un type de grain
Lorsqu’il existe plusieurs implémentations d’une interface de grain, comme dans l’exemple suivant, Orleans tente de déterminer l’implémentation prévue lors de la création d’une référence de grain. Prenons l’exemple suivant dans lequel il existe deux implémentations de l’interface ICounterGrain
:
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
}
L’appel suivant à GetGrain
lève une exception, car Orleans ne sait pas comment mapper ICounterGrain
sans ambiguïté à l’une des classes de grain.
// This will throw an exception: there is no unambiguous mapping from ICounterGrain to a grain class.
ICounterGrain myCounter = grainFactory.GetGrain<ICounterGrain>("my-counter");
Une erreur System.ArgumentException est levée avec le message suivant :
Unable to identify a single appropriate grain type for interface ICounterGrain. Candidates: upcounter (UpCounterGrain), downcounter (DownCounterGrain)
Le message d’erreur indique l’implémentation de grain Orleans qui correspond au type d’interface de grain demandé, ICounterGrain
. Il affiche les noms de types de grain (upcounter
et downcounter
), ainsi que les classes de grain (UpCounterGrain
et DownCounterGrain
).
Remarque
Les noms de type de grain dans le message d’erreur précédent, upcounter
et downcounter
, sont dérivés des noms de classes de grain, UpCounterGrain
et DownCounterGrain
respectivement. Il s’agit du comportement par défaut dans Orleans qui peut être personnalisé en ajoutant un attribut [GrainType(string)]
à la classe de grain. Par exemple :
[GrainType("up")]
public class UpCounterGrain : IUpCounterGrain { /* as above */ }
Il existe plusieurs façons de résoudre cette ambiguïté détaillée dans les sous-sections suivantes.
Ambiguïté des types de grain en utilisant des interfaces de marqueur uniques
La façon la plus claire de différencier ces grains est de leur donner des interfaces de grain uniques. Par exemple, si nous ajoutons l’interface IUpCounterGrain
à la classe UpCounterGrain
et que nous ajoutons l’interface IDownCounterGrain
à la classe DownCounterGrain
, comme dans l’exemple suivant, nous pouvons résoudre la référence de grain correcte en passant IUpCounterGrain
ou IDownCounterGrain
à l’appel GetGrain<T>
au lieu de passer le type ICounterGrain
ambigu.
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
}
Pour créer une référence à l’un des grains, tenez compte du code suivant :
// 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");
Remarque
Dans l’exemple précédent, vous avez créé deux références de grain avec la même clé, mais différents types de grain. La première, stockée dans la variable myUpCounter
, est une référence au grain avec l’ID upcounter/my-counter
. La deuxième, stockée dans la variable myDownCounter
, est une référence au grain avec l’ID downcounter/my-counter
. Il s’agit de la combinaison du type de grain et de la clé de grain qui identifient un grain de façon unique. Par conséquent, myUpCounter
et myDownCounter
font référence à différents grains.
Différencier des types de grain en fournissant un préfixe de classe de grain
Vous pouvez fournir un préfixe de nom de classe de grain à IGrainFactory.GetGrain, par exemple :
ICounterGrain myUpCounter = grainFactory.GetGrain<ICounterGrain>("my-counter", grainClassNamePrefix: "Up");
ICounterGrain myDownCounter = grainFactory.GetGrain<ICounterGrain>("my-counter", grainClassNamePrefix: "Down");
Spécification de l’implémentation d’un grain par défaut en utilisant la convention d’affectation de noms
Lors de la différenciation de plusieurs implémentations de la même interface de grain, Orleans sélectionne une implémentation en utilisant la convention de suppression du « I » de début à partir du nom de l’interface. Par exemple, si le nom de l’interface est ICounterGrain
et qu’il existe deux implémentations, CounterGrain
et DownCounterGrain
, Orleans choisit CounterGrain
lors de la demande de référence à ICounterGrain
, comme dans l’exemple suivant :
/// This will refer to an instance of CounterGrain, since that matches the convention.
ICounterGrain myUpCounter = grainFactory.GetGrain<ICounterGrain>("my-counter");
Spécification du type de grain par défaut en utilisant un attribut
L’attribut Orleans.Metadata.DefaultGrainTypeAttribute peut être ajouté à une interface de grain pour spécifier le type de grain de l’implémentation par défaut pour cette interface, comme dans l’exemple suivant :
[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");
Différenciation des types de grains en fournissant l’ID de grain résolu
Certaines surcharges de IGrainFactory.GetGrain acceptent un argument de type Orleans.Runtime.GrainId. Lors de l’utilisation de ces surcharges, Orleans n’a pas besoin de mapper d’un type d’interface à un type de grain et, par conséquent, il n’existe aucune différenciation à résoudre. Par exemple :
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"));