Condividi tramite


Riferimenti granulari

Prima di chiamare un metodo in base a una granularità, è necessario prima di tutto un riferimento a tale granularità. Un riferimento granulare è un oggetto proxy che implementa la stessa interfaccia granulare della classe granulare corrispondente. Incapsula l'identità logica (tipo e chiave univoca) del grain di destinazione. Si utilizzano riferimenti ai granuli per effettuare chiamate al granulo di destinazione. Ogni riferimento a un grano punta a un singolo grano (una singola istanza della classe grano), ma è possibile creare più riferimenti indipendenti allo stesso grano.

Poiché un riferimento granulare rappresenta l'identità logica della granularità di destinazione, è indipendente dalla posizione fisica della granularità e rimane valido anche dopo un riavvio completo del sistema. È possibile usare riferimenti granulari come qualsiasi altro oggetto .NET. È possibile passarlo a un metodo, usarlo come valore restituito dal metodo e salvarlo anche nell'archiviazione permanente.

È possibile ottenere un riferimento granulare passando l'identità di una granularità al IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) metodo , dove T è l'interfaccia di granularità ed key è la chiave univoca della granularità all'interno del relativo tipo.

Negli esempi seguenti viene illustrato come ottenere un riferimento granulare per l'interfaccia IPlayerGrain definita in precedenza.

Dall'interno di una classe di cereali:

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

Dal Orleans codice client:

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

I riferimenti granulari contengono tre informazioni:

  1. Tipo di granularità, che identifica in modo univoco la classe granulare.
  2. Chiave granulare, che identifica in modo univoco un'istanza logica di tale classe di granularità.
  3. Interfaccia che deve essere implementata dal riferimento granulare.

Annotazioni

Il tipo di granularità e la chiave formano l'identità granulare.

Si noti che le chiamate precedenti a IGrainFactory.GetGrain accettano solo due di queste tre cose:

  • Interfaccia implementata dal riferimento granulare, IPlayerGrain.
  • Chiave di granularità, ovvero il valore di playerId.

Nonostante dichiari che un riferimento al tipo di grano contenga un tipo, una chiave e un'interfaccia, gli esempi sono stati forniti Orleans solo con la chiave e l'interfaccia. Ciò è dovuto al fatto che Orleans mantiene una mappatura tra le interfacce di grain e i tipi di grain. Quando si chiede alla fabbrica dei granelli per IShoppingCartGrain, Orleans consulta la sua mappatura per trovare il tipo di granello corrispondente in modo che possa creare il riferimento. Questa operazione funziona quando è presente una sola implementazione di un'interfaccia grain. Tuttavia, se sono presenti più implementazioni, è necessario disambiguarle nella GetGrain chiamata. Per ulteriori informazioni, vedere la sezione successiva, Chiarimento della risoluzione dei tipi di granulo.

Annotazioni

Orleans genera tipi di implementazione di riferimento granulari per ogni interfaccia granulare nell'applicazione durante la compilazione. Queste implementazioni di riferimento granulare ereditano dalla Orleans.Runtime.GrainReference classe . GetGrain restituisce istanze dell'implementazione generata Orleans.Runtime.GrainReference corrispondente all'interfaccia granulare richiesta.

Risoluzione dei tipi di grano mediante disambiguazione

Quando esistono più implementazioni di un'interfaccia di granularità, ad esempio nell'esempio seguente, Orleans tenta di determinare l'implementazione prevista durante la creazione di un riferimento granulare. Si consideri l'esempio seguente, in cui sono presenti due implementazioni dell'interfaccia 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
}

La chiamata seguente a GetGrain genera un'eccezione perché Orleans non è in grado di eseguire il mapping ICounterGrain univoco a una delle classi di granularità.

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

Viene generata un'eccezione System.ArgumentException con il messaggio seguente:

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

Il messaggio di errore indica le implementazioni granulari rilevate Orleans che corrispondono al tipo di interfaccia granulare richiesto, ICounterGrain. Mostra i nomi dei tipi di granularità (upcounter e downcounter) e le classi di granularità (UpCounterGrain e DownCounterGrain).

Annotazioni

I nomi dei tipi di granularità nel messaggio di errore precedente e upcounterdowncounter, sono derivati rispettivamente dai nomi UpCounterGrainDownCounterGrain delle classi granulari. Si tratta del comportamento predefinito in Orleans e può essere personalizzato aggiungendo un [GrainType(string)] attributo alla classe grain. Per esempio:

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

Esistono diversi modi per risolvere questa ambiguità, descritta in dettaglio nelle sottosezioni seguenti.

Disambiguazione dei tipi di granularità tramite interfacce marcatori univoche

Il modo più chiaro per disambiguare questi grani consiste nel dare loro interfacce di granularità univoche. Ad esempio, se si aggiunge l'interfaccia IUpCounterGrain alla UpCounterGrain classe e si aggiunge l'interfaccia IDownCounterGrain alla DownCounterGrain classe , come nell'esempio seguente, è possibile risolvere il riferimento granulare corretto passando IUpCounterGrain o IDownCounterGrain alla GetGrain chiamata anziché al tipo ambiguo 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
}

Per creare un riferimento a uno dei due tipi di granularità, prendere in considerazione il codice seguente:

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

Annotazioni

Nell'esempio precedente sono stati creati due riferimenti granulari con la stessa chiave ma tipi di granularità diversi. Il primo, archiviato nella myUpCounter variabile , fa riferimento all'granularità con l'ID upcounter/my-counter. Il secondo, archiviato nella variabile myDownCounter, fa riferimento al grano con l'ID downcounter/my-counter. La combinazione di tipo di granularità e chiave di granularità identifica in modo univoco una granularità. Pertanto, myUpCounter e myDownCounter fare riferimento a grani diversi.

Disambiguare i tipi di granularità fornendo un prefisso di classe granulare

È possibile specificare un prefisso di nome di classe granulare a IGrainFactory.GetGrain, ad esempio:

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

Specifica dell'implementazione predefinita del grano tramite la convenzione di denominazione

Quando si disambiguano più implementazioni della stessa interfaccia di granularità, Orleans seleziona un'implementazione usando la convenzione di rimuovere un 'I' iniziale dal nome dell'interfaccia. Ad esempio, se il nome dell'interfaccia è ICounterGrain e sono presenti due implementazioni CounterGrain e DownCounterGrain, Orleans sceglie CounterGrain quando viene richiesto un riferimento a ICounterGrain, come nell'esempio seguente:

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

Specificare il tipo di granularità di default usando un attributo

È possibile aggiungere l'attributo a un'interfaccia Orleans.Metadata.DefaultGrainTypeAttribute granulare per specificare il tipo di granularità dell'implementazione predefinita per tale interfaccia, come illustrato nell'esempio seguente:

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

Disambiguare i tipi di granularità fornendo l'ID granulare risolto

Alcuni overload di IGrainFactory.GetGrain accettano un argomento di tipo Orleans.Runtime.GrainId. Quando si usano questi overload, Orleans non è necessario mappare da un tipo di interfaccia a un tipo di grain, quindi non c'è ambiguità da risolvere. Per esempio:

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