Compartilhar via


Referências de grãos

Antes de chamar um método em um grão, primeiro você precisa de uma referência a esse grão. Uma referência de grão é um objeto proxy que implementa a mesma interface de grão que a classe de grão correspondente. Ele encapsula a identidade lógica (tipo e chave exclusiva) do grão de destino. Você usa referências de grãos para fazer chamadas para o grão de destino. Cada referência de grão aponta para um único grão (uma única instância da classe de grãos), mas você pode criar várias referências independentes para o mesmo grão.

Como uma referência de grão representa a identidade lógica do grão de destino, ela é independente da localização física do grão e permanece válida mesmo após uma reinicialização completa do sistema. Você pode usar referências de grãos como qualquer outro objeto .NET. Você pode passá-lo para um método, usá-lo como um valor de retorno de método e até mesmo salvá-lo no armazenamento persistente.

Você pode obter uma referência de grão passando a identidade de um grão para o IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) método, onde T está a interface de grãos e key é a chave exclusiva do grão dentro de seu tipo.

Os exemplos a seguir mostram como obter uma referência de grãos para a IPlayerGrain interface definida anteriormente.

De dentro de uma classe de grãos:

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

Do código cliente Orleans:

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

As referências de grãos contêm três informações:

  1. O tipo de grão, que identifica exclusivamente a classe de grãos.
  2. A chave de grão, que identifica exclusivamente uma instância lógica dessa classe de grãos.
  3. A interface que a referência de grãos deve implementar.

Observação

O tipo de grão e a chave formam a identidade de grão.

Observe que as chamadas anteriores para IGrainFactory.GetGrain aceitaram apenas duas destas três coisas:

  • A interface implementada pela referência de grãos, IPlayerGrain.
  • A chave do grão, que é o valor de playerId.

Apesar de afirmar que uma referência de grão contém um tipo de grão, chave e interface, os exemplos são fornecidos Orleans apenas com a chave e a interface. Isso ocorre porque Orleans mantém um mapeamento entre interfaces de grãos e tipos de grãos. Quando você solicita IShoppingCartGrain à fábrica de grãos, Orleans consulta seu mapeamento para localizar o tipo de grão correspondente, a fim de criar a referência. Isso funciona quando há apenas uma implementação de uma interface de grãos. No entanto, se houver várias implementações, você precisará desambiguar na chamada GetGrain. Para obter mais informações, consulte a próxima seção, Resolução de Ambiguidade do Tipo de Grão.

Observação

Orleans gera tipos de implementação de referência de grãos para cada interface de grãos em seu aplicativo durante a compilação. Essas implementações de referência de grão herdam da classe Orleans.Runtime.GrainReference. GetGrain retorna instâncias da implementação gerada Orleans.Runtime.GrainReference correspondentes à interface de grãos solicitada.

Desambiguando a resolução do tipo de grão

Quando existem várias implementações de uma interface de grãos, como no exemplo a seguir, Orleans tenta determinar a implementação pretendida ao criar uma referência de grãos. Considere o exemplo a seguir, em que há duas implementações da ICounterGrain interface:

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
}

A chamada a seguir para GetGrain gera uma exceção porque Orleans não sabe mapear ICounterGrain com clareza para uma das classes de grãos.

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

Um System.ArgumentException é lançado com a seguinte mensagem:

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

A mensagem de erro informa quais implementações de grãos encontradas Orleans correspondem ao tipo ICounterGrainde interface de grão solicitado. Ele mostra os nomes de tipo de grão (upcounter e downcounter) e as classes de grãos (UpCounterGrain e DownCounterGrain).

Observação

Os nomes de tipo de grão na mensagem de erro anterior, upcounter e downcounter, são derivados dos nomes de classe de grão, UpCounterGrain e DownCounterGrain, respectivamente. Esse é o comportamento Orleans padrão e pode ser personalizado adicionando um [GrainType(string)] atributo à classe grain. Por exemplo:

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

Há várias maneiras de resolver essa ambiguidade, detalhadas nas subseções a seguir.

Desambiguando tipos de grãos usando interfaces de marcador exclusivas

A maneira mais clara de desambiguar esses grãos é proporcionando a eles interfaces exclusivas. Por exemplo, se você adicionar a interface IUpCounterGrain à UpCounterGrain classe e adicionar a interface IDownCounterGrain à DownCounterGrain classe, como no exemplo a seguir, poderá resolver a referência de grão correta passando IUpCounterGrain ou IDownCounterGrain para a GetGrain<T> chamada em vez do tipo ambíguo 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
}

Para criar uma referência a qualquer um dos grãos, considere o seguinte código:

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

Observação

No exemplo anterior, você criou duas referências de grãos com a mesma chave, mas tipos de grãos diferentes. O primeiro, armazenado na myUpCounter variável, faz referência ao grão com a ID upcounter/my-counter. O segundo, armazenado na myDownCounter variável, faz referência ao grão com a ID downcounter/my-counter. A combinação de tipo de grão e chave de grão identifica exclusivamente um grão. Portanto, myUpCounter e myDownCounter referem-se a diferentes tipos de grãos.

Desambiguando tipos de grãos fornecendo um prefixo de classe de grãos

Você pode fornecer um prefixo de nome de classe de grão para IGrainFactory.GetGrain, por exemplo:

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

Especificando a implementação de grão padrão usando a convenção de nomenclatura

Ao desambiguar várias implementações da mesma interface de grão, Orleans seleciona uma implementação usando a convenção de remover o 'I' inicial do nome da interface. Por exemplo, se o nome da interface for ICounterGrain e houver duas implementações, CounterGrain e DownCounterGrain, Orleans escolherá CounterGrain quando for solicitada uma referência a ICounterGrain, como no exemplo a seguir:

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

Especificando o tipo de grão padrão usando um atributo

Você pode adicionar o Orleans.Metadata.DefaultGrainTypeAttribute atributo a uma interface de grãos para especificar o tipo de grão da implementação padrão para essa interface, conforme mostrado no exemplo a seguir:

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

Desambiguando tipos de grãos fornecendo a ID de grão resolvida

Algumas sobrecargas de IGrainFactory.GetGrain aceitam um argumento do tipo Orleans.Runtime.GrainId. Ao usar essas sobrecargas, Orleans não precisa mapear de um tipo de interface para um tipo de grain, portanto, não há ambiguidade para resolver. Por exemplo:

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