Identidade de granularidade

Cada uma das granularidades em Orleans têm um identificador único, exclusivo e definido pelo usuário, que consiste em duas partes:

  1. O nome do tipo de granularidade, que identifica exclusivamente a classe de granularidade.
  2. A chave de granularidade, que identifica exclusivamente uma instância lógica dessa classe de granularidade.

O tipo e a chave de granularidade são representados como cadeias de caracteres legíveis por humanos em Orleans e, por convenção, a identidade de granularidade é escrita com o tipo e a chave de granularidade separados por um caractere /. Por exemplo, shoppingcart/bob65 representa o tipo de granularidade nomeado shoppingcart com uma chave bob65.

Não é comum construir identidades de granularidade diretamente. Em vez disso, é mais comum criar referências de granularidade usando Orleans.IGrainFactory.

As seções a seguir discutem nomes de tipos de granularidade e chaves de granularidade com mais detalhes.

Nomes dos tipos de granularidade

Orleans cria um nome de tipo de granularidade para você com base em sua classe de implementação de granularidade removendo o sufixo "Grain" do nome da classe, se ele estiver presente, e convertendo a cadeia de caracteres resultante em sua representação em minúsculas. Por exemplo, uma classe nomeada ShoppingCartGrain receberá o nome do tipo de granularidade shoppingcart. Recomenda-se que os nomes e as chaves dos tipos de granularidade consistam apenas em caracteres imprimíveis, como caracteres alfanuméricos (a-z, A-Z e 0, -9) e símbolos como-,_,@,=. Outros caracteres podem ou não ser suportados e muitas vezes precisarão de tratamento especial quando impressos em logs ou aparecerem como identificadores em outros sistemas, como bancos de dados.

Como alternativa, o atributo Orleans.GrainTypeAttribute pode ser usado para personalizar o nome do tipo de granularidade para a classe de granularidade à qual ele está anexado, como no exemplo a seguir:

[GrainType("cart")]
public class ShoppingCartGrain : IShoppingCartGrain
{
    // Add your grain implementation here
}

No exemplo anterior, a classe de granularidadeShoppingCartGrain tem um nome de tipo de granularidade de cart. Cada granularidade só pode ter um nome de tipo de granularidade.

Para granularidades genéricas, a aridade genérica deve ser incluída no nome do tipo de granularidade. Por exemplo, considere a classe DictionaryGrain<K, V> a seguir:

[GrainType("dict`2")]
public class DictionaryGrain<K, V> : IDictionaryGrain<K, V>
{
    // Add your grain implementation here
}

A classe de granularidade tem dois parâmetros genéricos, portanto, um backtick ` seguido pela aridade genérica, 2, é adicionado ao final do nome do tipo de granularidade, dict para criar o nome do tipo de granularidade dict`2, conforme especificado no atributo na classe de granularidade, [GrainType("dict`2")].

Chaves de granularidade

Por conveniência, Orleans expõe métodos que permitem a construção de chaves de granularidade a partir de um Guid ou um Int64, além de um String. A chave primária tem como escopo o tipo de granularidade. Portanto, a identidade completa de uma granularidade é formada do tipo do grão e de sua chave.

O chamador do grão decide qual esquema deve ser usado. As opções são:

Como os dados subjacentes são os mesmos, os esquemas podem ser usados de forma intercambiável: todos eles são codificados como cadeias de caracteres.

Situações que exigem uma instância de granularidade de singleton podem usar um valor fixo bem conhecido, como "default". Trata-se apenas de uma convenção, mas ao aderir a essa convenção fica claro no site do chamador que uma granularidade singleton está em uso.

Uso de GUIDs (identificadores globalmente exclusivos) como chaves

System.Guid fazer chaves úteis quando a aleatoriedade e a exclusividade global são desejadas, como ao criar um novo trabalho em um sistema de processamento de trabalho. Você não precisa coordenar a alocação de chaves, o que poderia introduzir um único ponto de falha no sistema, ou um bloqueio do lado do sistema em um recurso que possa apresentar um gargalo. Há uma chance muito baixa de colisão entre GUIDs, portanto, eles são uma escolha comum ao arquitetar um sistema que precisa alocar identificadores aleatórios.

Referenciando uma granularidade por GUID no código do cliente:

var grain = grainFactory.GetGrain<IExample>(Guid.NewGuid());

Recuperando a chave primária do código de granularidade:

public override Task OnActivateAsync()
{
    Guid primaryKey = this.GetPrimaryKey();
    return base.OnActivateAsync();
}

Usando inteiros como chaves

Um inteiro longo também está disponível, o que faria sentido se a granularidade fosse persistida em um banco de dados relacional, em que índices numéricos são preferenciais em vez de GUIDs.

Referenciando uma granularidade por um inteiro longo no código do cliente:

var grain = grainFactory.GetGrain<IExample>(1);

Recuperando a chave primária do código de granularidade:

public override Task OnActivateAsync()
{
    long primaryKey = this.GetPrimaryKeyLong();
    return base.OnActivateAsync();
}

Usando cadeias de caracteres como chaves

Uma cadeia de caracteres também está disponível.

Referenciando uma granularidade por cadeia de caracteres no código do cliente:

var grain = grainFactory.GetGrain<IExample>("myGrainKey");

Recuperando a chave primária do código de granularidade:

public override Task OnActivateAsync()
{
    string primaryKey = this.GetPrimaryKeyString();
    return base.OnActivateAsync();
}

Usando chaves compostas

Se você tiver um sistema que não se encaixa bem com GUIDs ou longos, pode optar por uma chave primária composta, que permite usar uma combinação de um GUID ou longo e uma cadeia de caracteres para fazer referência a uma granularidade.

Você pode herdar sua interface de IGrainWithGuidCompoundKey ou IGrainWithIntegerCompoundKey, da seguinte forma:

public interface IExampleGrain : Orleans.IGrainWithIntegerCompoundKey
{
    Task Hello();
}

No código do cliente, isso adiciona um segundo argumento ao método IGrainFactory.GetGrain na fábrica de granularidade:

var grain = grainFactory.GetGrain<IExample>(0, "a string!", null);

Para acessar a chave composta na granularidade, podemos chamar uma sobrecarga no método GrainExtensions.GetPrimaryKey (o GrainExtensions.GetPrimaryKeyLong):

public class ExampleGrain : Orleans.Grain, IExampleGrain
{
    public Task Hello()
    {
        long primaryKey = this.GetPrimaryKeyLong(out string keyExtension);
        Console.WriteLine($"Hello from {keyExtension}");

        Task.CompletedTask;
    }
}

Por que granularidades usam identificadores lógicos

Em ambientes orientados a objetos, como .NET, a identidade de um objeto é difícil de distinguir de uma referência a ele. Quando um objeto é criado usando a palavra-chave new, a referência que você recebe de volta representa todos os aspectos de sua identidade, exceto aqueles que mapeiam o objeto para alguma entidade externa que ele representa. Orleans é projetado para sistemas distribuídos. Em sistemas distribuídos, as referências a objetos não podem representar a identidade da instância, pois as referências a objetos são limitadas ao espaço de endereço de um único processo. Orleans usa identificadores lógicos para evitar essa limitação. As granularidades usam identificadores lógicos para que as referências de granularidade permaneçam válidas durante o tempo de vida do processo e sejam portáteis de um processo para outro, permitindo que sejam armazenadas e posteriormente recuperadas ou enviadas através de uma rede para outro processo no aplicativo, tudo isso enquanto ainda se referem à mesma entidade: a granularidade para a qual a referência foi criada.