Odwołania do ziarna
Przed wywołaniem metody na ziarnie należy najpierw odwołać się do tego ziarna. Odwołanie do ziarna to obiekt proxy, który implementuje ten sam interfejs ziarna co odpowiednia klasa ziarna. Hermetyzuje tożsamość logiczną (typ i unikatowy klucz) ziarna docelowego. Odwołania do ziarna służą do wykonywania wywołań do ziarna docelowego. Każde odwołanie ziarna jest pojedynczym ziarnem (pojedynczym wystąpieniem klasy ziarna), ale można utworzyć wiele niezależnych odwołań do tego samego ziarna.
Ponieważ odwołanie ziarna reprezentuje logiczną tożsamość ziarna docelowego, jest niezależna od fizycznej lokalizacji ziarna i pozostaje prawidłowa nawet po pełnym ponownym uruchomieniu systemu. Deweloperzy mogą używać odwołań ziarna, takich jak każdy inny obiekt platformy .NET. Można go przekazać do metody, używaną jako wartość zwracaną przez metodę, a nawet zapisywaną w magazynie trwałym.
Odwołanie do ziarna można uzyskać, przekazując tożsamość ziarna do IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) metody, gdzie T
jest interfejsem ziarna i key
jest unikatowym kluczem ziarna w obrębie typu.
Poniżej przedstawiono przykłady uzyskiwania odwołania do ziarna interfejsu zdefiniowanego IPlayerGrain
powyżej.
Z poziomu klasy ziarna:
// This would typically be read from an HTTP request parameter or elsewhere.
Guid playerId = Guid.NewGuid();
IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);
Z Orleans poziomu kodu klienta:
// This would typically be read from an HTTP request parameter or elsewhere.
Guid playerId = Guid.NewGuid();
IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);
Odwołania ziarna zawierają trzy informacje:
- Typ ziarna, który jednoznacznie identyfikuje klasę ziarna.
- Klucz ziarna, który jednoznacznie identyfikuje logiczne wystąpienie tej klasy ziarna.
- Interfejs, który musi implementować odwołanie ziarna.
Uwaga
Typ ziarna i klucz tworzą tożsamość ziarna.
Zwróć uwagę, że powyższe wywołania IGrainFactory.GetGrain do akceptowanych tylko dwóch z tych trzech elementów:
- Interfejs zaimplementowany przez odwołanie do ziarna,
IPlayerGrain
. - Klucz ziarna, który jest wartością
playerId
.
Pomimo stwierdzenia, że odwołanie do ziarna zawiera typ ziarna, klucz i interfejs, przykłady dostarczane Orleans tylko z kluczem i interfejsem. Dzieje się tak, ponieważ Orleans utrzymuje mapowanie między interfejsami ziarna i typami ziarna. Gdy poprosisz fabrykę ziarna o IShoppingCartGrain
, Orleans skonsultuje się z mapowaniem, aby znaleźć odpowiedni typ ziarna, aby można było utworzyć odwołanie. To działa, gdy istnieje tylko jedna implementacja interfejsu ziarna, ale jeśli istnieje wiele implementacji, konieczne będzie ich uściślanie w wywołaniu GetGrain
. Aby uzyskać więcej informacji, zobacz następną sekcję, uściślającą rozdzielczość typu ziarna.
Uwaga
Orleans generuje typy implementacji odwołań ziarna dla każdego interfejsu ziarna w aplikacji podczas kompilacji. Te implementacje odwołań ziarna dziedziczą po Orleans.Runtime.GrainReference klasie. GetGrain
zwraca wystąpienia wygenerowanej Orleans.Runtime.GrainReference implementacji odpowiadające żądanemu interfejsowi ziarna.
Uściślanie rozdzielczości typu ziarna
Jeśli istnieje wiele implementacji interfejsu ziarna, na przykład w poniższym przykładzie, Orleans próbuje określić docelową implementację podczas tworzenia odwołania do ziarna. Rozważmy następujący przykład, w którym istnieją dwie implementacje interfejsu 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
}
Następujące wywołanie GetGrain
metody zgłosi wyjątek, ponieważ Orleans nie wie, jak jednoznacznie mapować ICounterGrain
na jedną z klas ziarna.
// This will throw an exception: there is no unambiguous mapping from ICounterGrain to a grain class.
ICounterGrain myCounter = grainFactory.GetGrain<ICounterGrain>("my-counter");
Zostanie System.ArgumentException zgłoszony następujący komunikat:
Unable to identify a single appropriate grain type for interface ICounterGrain. Candidates: upcounter (UpCounterGrain), downcounter (DownCounterGrain)
Komunikat o błędzie informuje o implementacji Orleans ziarna, która jest zgodna z żądanym typem interfejsu ziarna, ICounterGrain
. Przedstawia nazwy typów ziarna (upcounter
i downcounter
), a także klasy ziarna (UpCounterGrain
i DownCounterGrain
).
Uwaga
Nazwy typów ziarna w poprzednim komunikacie upcounter
o błędzie i downcounter
, pochodzą odpowiednio z nazw UpCounterGrain
klas ziarna i DownCounterGrain
. Jest to domyślne zachowanie w Orleans programie i można go dostosować przez dodanie [GrainType(string)]
atrybutu do klasy ziarna. Na przykład:
[GrainType("up")]
public class UpCounterGrain : IUpCounterGrain { /* as above */ }
Istnieje kilka sposobów rozwiązania tej niejednoznaczności opisanej w poniższych podsekcjach.
Uściślanie typów ziarna przy użyciu unikatowych interfejsów znaczników
Najczystszym sposobem uściślania tych ziarna jest nadanie im unikatowych interfejsów ziarna. Jeśli na przykład dodamy interfejs do UpCounterGrain
klasy i dodamy interfejs IUpCounterGrain
IDownCounterGrain
do DownCounterGrain
klasy, podobnie jak w poniższym przykładzie, możemy rozpoznać poprawne odwołanie ziarna przez przekazanie IUpCounterGrain
wywołania lub IDownCounterGrain
GetGrain<T>
wywołanie zamiast przekazywania niejednoznacznego ICounterGrain
typu.
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
}
Aby utworzyć odwołanie do dowolnego ziarna, rozważ następujący kod:
// 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");
Uwaga
W poprzednim przykładzie utworzono dwa odwołania do ziarna z tym samym kluczem, ale różne typy ziarna. Pierwszy, przechowywany w zmiennej myUpCounter
, jest odwołaniem do ziarna o identyfikatorze upcounter/my-counter
. Drugi, przechowywany w zmiennej myDownCounter
, jest odwołaniem do ziarna o identyfikatorze downcounter/my-counter
. Jest to kombinacja typu ziarna i klucza ziarna, które jednoznacznie identyfikują ziarno. W związku z tym, myUpCounter
i myDownCounter
odnosić się do różnych ziarna.
Uściślanie typów ziarna przez podanie prefiksu klasy ziarna
Możesz podać prefiks nazwy klasy ziarna na IGrainFactory.GetGrain, na przykład:
ICounterGrain myUpCounter = grainFactory.GetGrain<ICounterGrain>("my-counter", grainClassNamePrefix: "Up");
ICounterGrain myDownCounter = grainFactory.GetGrain<ICounterGrain>("my-counter", grainClassNamePrefix: "Down");
Określanie domyślnej implementacji ziarna przy użyciu konwencji nazewnictwa
Podczas uściślania wielu implementacji tego samego interfejsu Orleans ziarna wybierze implementację przy użyciu konwencji usuwania wiodącego "I" z nazwy interfejsu. Jeśli na przykład nazwa interfejsu to ICounterGrain
i istnieją dwie implementacje, CounterGrain
i DownCounterGrain
, Orleans wybierze CounterGrain
, gdy zostanie wyświetlony monit o odwołanie do ICounterGrain
elementu , jak w poniższym przykładzie:
/// This will refer to an instance of CounterGrain, since that matches the convention.
ICounterGrain myUpCounter = grainFactory.GetGrain<ICounterGrain>("my-counter");
Określanie domyślnego typu ziarna przy użyciu atrybutu
Atrybut Orleans.Metadata.DefaultGrainTypeAttribute można dodać do interfejsu ziarna, aby określić typ ziarna domyślnej implementacji dla tego interfejsu, jak w poniższym przykładzie:
[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");
Uściślanie typów ziarna przez podanie rozpoznanych identyfikatorów ziarna
Niektóre przeciążenia akceptowania IGrainFactory.GetGrain argumentu typu Orleans.Runtime.GrainId. W przypadku używania tych przeciążeń Orleans nie trzeba mapować typu interfejsu na typ ziarna i dlatego nie ma wątpliwości, które należy rozwiązać. Na przykład:
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"));