Partage via


Références de céréale

Avant d’appeler une méthode sur un grain, vous avez d’abord besoin d’une référence à ce grain. Une référence de grain est un objet proxy implémentant la même interface de grain que la classe de grain correspondante. Il encapsule l’identité logique (type et clé unique) du grain cible. Vous utilisez des références de grain pour effectuer des appels au grain cible. Chaque référence de grain pointe vers un seul grain (une seule instance de la classe de grain), mais vous pouvez 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. Vous pouvez utiliser des références de grain comme n’importe quel autre objet .NET. Vous pouvez le transmettre à une méthode, l’utiliser comme valeur de retour de méthode, et même l’enregistrer dans un stockage persistant.

Vous pouvez obtenir une référence de grain en transmettant 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 son type.

Les exemples suivants montrent comment obtenir une référence de grain pour l’interface IPlayerGrain définie précédemment.

À 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 Orleans code client :

// 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 :

  1. Type de grain, qui identifie de façon unique la classe de grain.
  2. Clé de grain, qui identifie de façon unique une instance logique de cette classe de grain.
  3. 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 précédents de IGrainFactory.GetGrain n'ont accepté que deux de ces trois choses :

  • Interface implémentée par la référence de grain, IPlayerGrain.
  • La clé de grain, qui est la valeur de .

Bien qu’il soit indiqué qu’une référence de grain contient un type de grain, une clé et une interface, les exemples fournis n'incluent que la Orleans 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 à la fabrique de grains IShoppingCartGrain, Orleans consulte son mappage pour identifier le type de grain correspondant afin de pouvoir créer la référence. Cela fonctionne lorsqu’il n’existe qu’une seule implémentation d’une interface de grain. Cependant, s’il existe plusieurs implémentations, vous devez les désambiguïser dans l’appel GetGrain. Pour plus d’informations, consultez la section suivante, désambiguïsation de la résolution des types de grain.

Remarque

Orleans génère, lors de la compilation, des types d'implémentation de référence pour chaque interface de grain dans votre application. Ces implémentations de référence de grain héritent de la Orleans.Runtime.GrainReference classe. GetGrain retourne des instances de l’implémentation générée Orleans.Runtime.GrainReference correspondant à l’interface grain demandée.

Résolution des ambiguïtés de type de grain

Quand plusieurs implémentations d’une interface de grain existent, 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, où 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 provoque une exception, car Orleans ne sait pas comment associer clairement ICounterGrain à 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 System.ArgumentException est déclenché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 vous indique les implémentations de grain trouvées Orleans qui correspondent au type d’interface grain demandé. ICounterGrain Il affiche les noms de types de grain (upcounter et downcounter) et les classes de grain (UpCounterGrain et DownCounterGrain).

Remarque

Les noms de type de grain dans le message d’erreur précédent et upcounterdowncounter, sont dérivés des noms de classes de grain, UpCounterGrain et DownCounterGrain respectivement. Il s’agit du comportement par défaut dans Orleans lequel il peut être personnalisé en ajoutant un [GrainType(string)] attribut à 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.

Clarification des types de grain à l’aide d’interfaces de marqueur uniques

La façon la plus claire de lever l’ambiguïté de ces grains est de leur donner des interfaces de grain uniques. Par exemple, si vous ajoutez l’interface IUpCounterGrain à la UpCounterGrain classe et ajoutez l’interface IDownCounterGrain à la DownCounterGrain classe, comme dans l’exemple suivant, vous pouvez résoudre la référence de grain correcte en passant IUpCounterGrain ou IDownCounterGrain à l’appel GetGrain au lieu du type ambigu ICounterGrain .

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 ou l’autre 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 grains avec la même clé, mais différents types de grain. La première, stockée dans la myUpCounter variable, fait référence au grain avec l’ID upcounter/my-counter. Le deuxième, stocké dans la myDownCounter variable, fait référence au grain avec l’ID downcounter/my-counter. La combinaison du type de grain et de la clé de grain identifie de façon unique un grain. Par conséquent, myUpCounter et myDownCounter font référence à différents grains.

Lever l’ambiguïté 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 de grain par défaut à l’aide de la convention d’affectation de noms

Lorsqu'il s'agit de différencier plusieurs implémentations de la même interface grain, Orleans sélectionne une implémentation selon la convention consistant à supprimer le « I » initial 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 lorsqu'on lui demande une 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 à l’aide d’un attribut

Vous pouvez ajouter l’attribut Orleans.Metadata.DefaultGrainTypeAttribute à une interface de grain pour spécifier le type de grain de l’implémentation par défaut pour cette interface, comme illustré 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");

Désambiguïsation des types de grains par la fourniture de l'ID de type 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, il n’y a donc aucune ambiguïté à 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"));