Migrowanie z Orleans wersji 3.x do wersji 7.0

Orleans 7.0 wprowadza kilka korzystnych zmian, w tym ulepszenia hostingu, serializacji niestandardowej, niezmienności i abstrakcji ziarna.

Migracja

Istniejące aplikacje korzystające z przypomnień, strumieni lub trwałości ziarna nie mogą być łatwo migrowane do Orleans wersji 7.0 ze względu na zmiany sposobu Orleans identyfikowania ziarna i strumieni. Planujemy przyrostowe oferowanie ścieżki migracji dla tych aplikacji.

Aplikacje z poprzednimi Orleans wersjami programu nie mogą być bezproblemowo uaktualniane za pośrednictwem uaktualnienia stopniowego do Orleans wersji 7.0. W związku z tym należy użyć innej strategii uaktualniania, takiej jak wdrażanie nowego klastra i likwidowanie poprzedniego klastra. Orleans 7.0 zmienia protokół przewodowy w niezgodny sposób, co oznacza, że klastry nie mogą zawierać kombinacji Orleans hostów 7.0 i hostów z poprzednimi Orleanswersjami programu .

Unikaliśmy takich zmian powodujących niezgodność od wielu lat, nawet w przypadku dużych wydań, więc dlaczego teraz? Istnieją dwa główne przyczyny: tożsamości i serializacji. W odniesieniu do tożsamości tożsamości, tożsamości ziarna i strumienia składają się teraz z ciągów, dzięki czemu ziarna mogą poprawnie kodować ogólne informacje o typie i zezwalać strumieniom na łatwiejsze mapowanie do domeny aplikacji. Typy ziarna zostały wcześniej zidentyfikowane przy użyciu złożonej struktury danych, która nie może reprezentować ogólnych ziarna, co prowadzi do przypadków narożnych. Strumienie zostały zidentyfikowane przez string przestrzeń nazw i Guid klucz, który był trudny dla deweloperów do mapowania na domenę aplikacji, jednak wydajny. Serializacja jest teraz odporna na wersje, co oznacza, że można modyfikować typy w określony zgodny sposób, zgodnie z zestawem reguł i mieć pewność, że możesz uaktualnić aplikację bez błędów serializacji. Było to szczególnie problematyczne, gdy typy aplikacji utrwalone w strumieniach lub magazynie ziarna. W poniższych sekcjach szczegółowo opisano główne zmiany i bardziej szczegółowo omówiono je.

Tworzenie pakietów zmian

W przypadku uaktualniania projektu do Orleans wersji 7.0 należy wykonać następujące czynności:

Napiwek

Wszystkie próbki Orleans zostały uaktualnione do Orleans wersji 7.0 i mogą być używane jako odwołanie do wprowadzonych zmian. Aby uzyskać więcej informacji, zobacz Orleans problem nr 8035 określający zmiany wprowadzone w poszczególnych przykładach.

Orleansglobal using Dyrektyw

Wszystkie Orleans projekty bezpośrednio lub pośrednio odwołują się do Microsoft.Orleans.Sdk pakietu NuGet. Po skonfigurowaniu Orleans projektu w celu włączenia niejawnych metod używania (na przykład <ImplicitUsings>enable</ImplicitUsings>) Orleans przestrzenie nazw i Orleans.Hosting są używane niejawnie. Oznacza to, że kod aplikacji nie potrzebuje tych dyrektyw.

Aby uzyskać więcej informacji, zobacz ImplicitUsings and dotnet/orleans/src/Orleans. Zestaw SDK/kompilacja/Microsoft.Orleans. Sdk.targets.

Hosting

Typ ClientBuilder został zastąpiony UseOrleansClient metodą rozszerzenia w pliku IHostBuilder. Typ IHostBuilder pochodzi z pakietu NuGet Microsoft.Extensions.Hosting . Oznacza to, że można dodać Orleans klienta do istniejącego hosta bez konieczności tworzenia oddzielnego kontenera iniekcji zależności. Klient nawiązuje połączenie z klastrem podczas uruchamiania. Po IHost.StartAsync zakończeniu klient zostanie automatycznie połączony. Usługi dodane do IHostBuilder elementu są uruchamiane w kolejności rejestracji, dlatego wywołanie UseOrleansClient przed wywołaniem ConfigureWebHostDefaults zapewni Orleans uruchomienie przed rozpoczęciem ASP.NET Core na przykład, co umożliwi natychmiastowe uzyskanie dostępu do klienta z aplikacji ASP.NET Core.

Jeśli chcesz emulować poprzednie ClientBuilder zachowanie, możesz utworzyć oddzielną i HostBuilder skonfigurować ją za pomocą Orleans klienta. IHostBuilder może mieć skonfigurowanego Orleans klienta lub silosu Orleans . Wszystkie silosy rejestrują wystąpienie IGrainFactory programu i IClusterClient którego aplikacja może używać, dlatego oddzielne konfigurowanie klienta jest niepotrzebne i nieobsługiwane.

OnActivateAsync i OnDeactivateAsync zmiana podpisu

Orleans umożliwia ziarnom wykonywanie kodu podczas aktywacji i dezaktywacji. Może to służyć do wykonywania zadań, takich jak odczytywanie stanu z komunikatów dotyczących magazynu lub cyklu życia dziennika. W Orleans wersji 7.0 podpis tych metod cyklu życia uległ zmianie:

  • OnActivateAsync() teraz akceptuje CancellationToken parametr. Po anulowaniu CancellationToken procesu aktywacji należy zrezygnować.
  • OnDeactivateAsync() Teraz akceptuje DeactivationReason parametr i CancellationToken parametr. Wskazuje DeactivationReason , dlaczego aktywacja jest dezaktywowana. Deweloperzy powinni używać tych informacji do celów rejestrowania i diagnostyki. Po anulowaniu CancellationToken procesu dezaktywacji należy wykonać natychmiast. Należy pamiętać, że ponieważ każdy host może zakończyć się niepowodzeniem w dowolnym momencie, nie zaleca się polegania na OnDeactivateAsync wykonywaniu ważnych akcji, takich jak utrwalanie stanu krytycznego.

Rozważmy następujący przykład przesłonięcia tych nowych metod:

public sealed class PingGrain : Grain, IPingGrain
{
    private readonly ILogger<PingGrain> _logger;

    public PingGrain(ILogger<PingGrain> logger) =>
        _logger = logger;

    public override Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("OnActivateAsync()");
        return Task.CompletedTask;
    }

    public override Task OnDeactivateAsync(DeactivationReason reason, CancellationToken token)
    {
        _logger.LogInformation("OnDeactivateAsync({Reason})", reason);
        return Task.CompletedTask;
    }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

Ziarna POCO i IGrainBase

Ziarna w Orleans nie muszą już dziedziczyć z klasy bazowej Grain ani jakiejkolwiek innej klasy. Ta funkcja jest określana jako ziarna POCO . Aby uzyskać dostęp do metod rozszerzeń, takich jak dowolne z następujących:

Ziarno musi implementować IGrainBase lub dziedziczyć z Grain. Oto przykład implementacji IGrainBase klasy ziarna:

public sealed class PingGrain : IGrainBase, IPingGrain
{
    public PingGrain(IGrainContext context) => GrainContext = context;

    public IGrainContext GrainContext { get; }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

IGrainBase Ponadto definiuje OnActivateAsync i OnDeactivateAsync z domyślnymi implementacjami, dzięki czemu ziarno może uczestniczyć w jego cyklu życia w razie potrzeby:

public sealed class PingGrain : IGrainBase, IPingGrain
{
    private readonly ILogger<PingGrain> _logger;

    public PingGrain(IGrainContext context, ILogger<PingGrain> logger)
    {
        _logger = logger;
        GrainContext = context;
    }

    public IGrainContext GrainContext { get; }

    public Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("OnActivateAsync()");
        return Task.CompletedTask;
    }

    public Task OnDeactivateAsync(DeactivationReason reason, CancellationToken token)
    {
        _logger.LogInformation("OnDeactivateAsync({Reason})", reason);
        return Task.CompletedTask;
    }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

Serializacja

Najbardziej uciążliwą zmianą w Orleans wersji 7.0 jest wprowadzenie serializatora odpornego na wersję. Ta zmiana została wprowadzona, ponieważ aplikacje mają tendencję do rozwoju i doprowadziło to do znacznej pułapki dla deweloperów, ponieważ poprzedni serializator nie mógł tolerować dodawania właściwości do istniejących typów. Z drugiej strony serializator był elastyczny, dzięki czemu deweloperzy mogą reprezentować większość typów platformy .NET bez modyfikacji, w tym funkcje takie jak typy ogólne, polimorfizm i śledzenie odwołań. Zastąpienie było dawno zaległe, ale użytkownicy nadal potrzebują wysokiej wierności reprezentacji ich typów. W związku z tym wprowadzono serializator zastępczy w Orleans wersji 7.0, która obsługuje reprezentację wysokiej wierności typów platformy .NET, jednocześnie umożliwiając rozwój typów. Nowy serializator jest znacznie bardziej wydajny niż poprzedni serializator, co daje do 170% wyższą przepływność kompleksową.

Aby uzyskać więcej informacji, zobacz następujące artykuły dotyczące Orleans wersji 7.0:

Tożsamości ziarna

Ziarna mają unikatową tożsamość, która składa się z typu ziarna i jego klucza. Poprzednie wersje używane typu złożonego Orleans dla s do GrainIdobsługi kluczy ziarna jednego z następujących elementów:

Wiąże się to z pewną złożonością, jeśli chodzi o radzenie sobie z kluczami ziarna. Tożsamości ziarna składają się z dwóch składników: typu i klucza. Składnik typu składał się wcześniej z kodu typu liczbowego, kategorii i 3 bajtów ogólnych informacji o typie.

Tożsamości ziarna mają teraz postać type/key , w której zarówno ciągi, jak type i key są. Najczęściej używanym interfejsem IGrainWithStringKeyklucza ziarna jest . Znacznie upraszcza to działanie tożsamości ziarna i zwiększa obsługę typów ziarna ogólnego.

Interfejsy ziarna są teraz reprezentowane przy użyciu nazwy czytelnej dla człowieka, a nie kombinacji kodu skrótu i reprezentacji ciągu dowolnego ogólnego typu parametrów.

Nowy system jest bardziej dostosowywalny, a te dostosowania mogą być oparte na atrybutach.

  • GrainTypeAttribute(String) na ziarnie class określa część Typ identyfikatora ziarna.
  • DefaultGrainTypeAttribute(String) w ziarnie interface określa typ ziarna, który IGrainFactory powinien być rozpoznawany domyślnie podczas uzyskiwania odwołania ziarna. Na przykład podczas wywoływania IGrainFactory.GetGrain<IMyGrain>("my-key")fabryki ziarna zwróci odwołanie do ziarna "my-type/my-key" , jeśli IMyGrain określono wyżej określony atrybut.
  • GrainInterfaceTypeAttribute(String) umożliwia zastąpienie nazwy interfejsu. Jawne określenie nazwy przy użyciu tego mechanizmu umożliwia zmianę nazwy typu interfejsu bez przerywania zgodności z istniejącymi odwołaniami ziarna. Należy pamiętać, że interfejs powinien również mieć AliasAttribute wartość w tym przypadku, ponieważ jego tożsamość może być serializowana. Aby uzyskać więcej informacji na temat określania aliasu typu, zobacz sekcję dotyczącą serializacji.

Jak wspomniano powyżej, zastąpienie domyślnych nazw klas ziarna i interfejsów dla typów umożliwia zmianę nazw bazowych typów bez przerywania zgodności z istniejącymi wdrożeniami.

Tożsamości strumienia

Po Orleans pierwszym wydaniu strumieni można zidentyfikować strumienie tylko przy użyciu elementu Guid. Było to wydajne pod względem alokacji pamięci, ale trudno było użytkownikom tworzyć znaczące tożsamości strumienia, często wymagając kodowania lub pośredniego określenia odpowiedniej tożsamości strumienia w danym celu.

W Orleans wersji 7.0 strumienie są teraz identyfikowane przy użyciu ciągów. Zawiera Orleans.Runtime.StreamIdstruct trzy właściwości: , StreamId.Namespace, i StreamId.KeyStreamId.FullKey. Te wartości właściwości są kodowane ciągami UTF-8. Na przykład StreamId.Create(String, String).

Zamiana simpleMessage Strumienie na BroadcastChannel

SimpleMessageStreams (nazywany również sms) został usunięty w wersji 7.0. Program SMS miał ten sam interfejs co Orleans.Providers.Streams.PersistentStreams, ale jego zachowanie było bardzo różne, ponieważ polegało na bezpośrednich wywołaniach ziarna do ziarna. Aby uniknąć nieporozumień, wiadomość SMS została usunięta i wprowadzono nową nazwę zastępczą Orleans.BroadcastChannel .

BroadcastChannel Obsługuje tylko niejawne subskrypcje i może być bezpośrednim zastąpieniem w tym przypadku. Jeśli potrzebujesz jawnych subskrypcji lub musisz użyć interfejsu PersistentStream (na przykład używano programu SMS w testach podczas korzystania z usługi EventHub w środowisku produkcyjnym), MemoryStream najlepszym kandydatem jest dla Ciebie.

BroadcastChannel będą miały takie same zachowania jak sms, podczas gdy MemoryStream będą zachowywać się jak inni dostawcy strumienia. Rozważmy następujący przykład użycia kanału emisji:

// Configuration
builder.AddBroadcastChannel(
    "my-provider",
    options => options.FireAndForgetDelivery = false);

// Publishing
var grainKey = Guid.NewGuid().ToString("N");
var channelId = ChannelId.Create("some-namespace", grainKey);
var stream = provider.GetChannelWriter<int>(channelId);

await stream.Publish(1);
await stream.Publish(2);
await stream.Publish(3);

// Simple implicit subscriber example
[ImplicitChannelSubscription]
public sealed class SimpleSubscriberGrain : Grain, ISubscriberGrain, IOnBroadcastChannelSubscribed
{
    // Called when a subscription is added to the grain
    public Task OnSubscribed(IBroadcastChannelSubscription streamSubscription)
    {
        streamSubscription.Attach<int>(
          item => OnPublished(streamSubscription.ChannelId, item),
          ex => OnError(streamSubscription.ChannelId, ex));

        return Task.CompletedTask;

        // Called when an item is published to the channel
        static Task OnPublished(ChannelId id, int item)
        {
            // Do something
            return Task.CompletedTask;
        }

        // Called when an error occurs
        static Task OnError(ChannelId id, Exception ex)
        {
            // Do something
            return Task.CompletedTask;
        }
    }
}

Migracja do MemoryStream będzie łatwiejsza, ponieważ tylko konfiguracja musi ulec zmianie. Rozważ następującą MemoryStream konfigurację:

builder.AddMemoryStreams<DefaultMemoryMessageBodySerializer>(
    "in-mem-provider",
    _ =>
    {
        // Number of pulling agent to start.
        // DO NOT CHANGE this value once deployed, if you do rolling deployment
        _.ConfigurePartitioning(partitionCount: 8);
    });

OpenTelemetry

System telemetrii został zaktualizowany w Orleans wersji 7.0, a poprzedni system został usunięty na rzecz ustandaryzowanych interfejsów API platformy .NET, takich jak metryki platformy .NET dla metryk i ActivitySource śledzenia.

W ramach tej części istniejące Microsoft.Orleans.TelemetryConsumers.* pakiety zostały usunięte. Rozważamy wprowadzenie nowego zestawu pakietów, aby usprawnić proces integrowania metryk emitowanych przez Orleans użytkownika z wybranym rozwiązaniem do monitorowania. Jak zawsze, opinie i współtworzenie są mile widziane.

Narzędzie dotnet-counters oferuje monitorowanie wydajności na potrzeby monitorowania kondycji ad hoc i badania wydajności pierwszego poziomu. W przypadku Orleans liczników narzędzie dotnet-counters może służyć do ich monitorowania:

dotnet counters monitor -n MyApp --counters Microsoft.Orleans

Podobnie metryki OpenTelemetry mogą dodawać mierniki Microsoft.Orleans , jak pokazano w poniższym kodzie:

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics => metrics
        .AddPrometheusExporter()
        .AddMeter("Microsoft.Orleans"));

Aby włączyć śledzenie rozproszone, należy skonfigurować funkcję OpenTelemetry, jak pokazano w poniższym kodzie:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.SetResourceBuilder(ResourceBuilder.CreateDefault()
            .AddService(serviceName: "ExampleService", serviceVersion: "1.0"));

        tracing.AddAspNetCoreInstrumentation();
        tracing.AddSource("Microsoft.Orleans.Runtime");
        tracing.AddSource("Microsoft.Orleans.Application");

        tracing.AddZipkinExporter(options =>
        {
            options.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
        });
    });

W poprzednim kodzie funkcja OpenTelemetry jest skonfigurowana do monitorowania:

  • Microsoft.Orleans.Runtime
  • Microsoft.Orleans.Application

Aby propagować działanie, wywołaj metodę AddActivityPropagation:

builder.Host.UseOrleans((_, clientBuilder) =>
{
    clientBuilder.AddActivityPropagation();
});

Refaktoryzacja funkcji z pakietu podstawowego do oddzielnych pakietów

W Orleans wersji 7.0 podjęliśmy wysiłek, aby uwzględnić rozszerzenia w oddzielne pakiety, które nie korzystają z programu Orleans.Core. Mianowicie , Orleans.StreamingOrleans.Remindersi Orleans.Transactions zostały oddzielone od rdzenia. Oznacza to, że te pakiety są całkowicie opłacane za to, czego używasz, i żaden kod w rdzeniu programu Orleans nie jest przeznaczony dla tych funkcji. Pozwala to obniżyć rozmiar podstawowego interfejsu API i zestawu, uprościć rdzeń i zwiększyć wydajność. W odniesieniu do wydajności transakcje w Orleans poprzednio wymagały kodu, który został wykonany dla każdej metody w celu koordynowania potencjalnych transakcji. Od tego czasu zostało to przeniesione do poszczególnych metod.

Jest to zmiana powodująca niezgodność kompilacji. Być może masz istniejący kod, który współdziała z przypomnieniami lub strumieniami, wywołując metody, które zostały wcześniej zdefiniowane w klasie bazowej Grain , ale są teraz metodami rozszerzenia. Takie wywołania, które nie określają this (na GetRemindersprzykład ), należy zaktualizować w celu uwzględnienia this (na przykład this.GetReminders()), ponieważ metody rozszerzeń muszą być kwalifikowane. Jeśli nie zaktualizujesz tych wywołań, wystąpi błąd kompilacji, a wymagana zmiana kodu może nie być oczywista, jeśli nie wiesz, co się zmieniło.

Klient transakcji

Orleans 7.0 wprowadza nową abstrakcję do koordynowania transakcji, Orleans.ITransactionClient. Wcześniej transakcje mogły być koordynowane tylko przez ziarna. Dzięki ITransactionClientfunkcji , która jest dostępna za pośrednictwem wstrzykiwania zależności, klienci mogą również koordynować transakcje bez konieczności pośredniczącego ziarna. Poniższy przykład wycofuje środki z jednego konta i deponuje je do innej w ramach jednej transakcji. Ten kod może być wywoływany z poziomu ziarna lub z klienta zewnętrznego, który pobrał element ITransactionClient z kontenera wstrzykiwania zależności.

await transactionClient.RunTransaction(
  TransactionOption.Create,
  () => Task.WhenAll(from.Withdraw(100), to.Deposit(100)));

W przypadku transakcji koordynowanych przez klienta klient musi dodać wymagane usługi w czasie konfiguracji:

clientBuilder.UseTransactions();

W przykładzie BankAccount pokazano użycie obiektu ITransactionClient. Aby uzyskać więcej informacji, zobacz Orleans transakcje.

Ponowne wywołania łańcucha wywołań

Ziarna są domyślnie jednowątkowy i przetwarzają żądania po jednym od początku do końca. Innymi słowy, ziarna nie są domyślnie powtarzane. Dodanie elementu ReentrantAttribute do klasy ziarna umożliwia równoczesne przetwarzanie wielu żądań w sposób przeplatania, przy jednoczesnym zachowaniu pojedynczego wątku. Może to być przydatne w przypadku ziarna, które nie posiadają stanu wewnętrznego lub wykonują wiele operacji asynchronicznych, takich jak wydawanie wywołań HTTP lub zapisywanie w bazie danych. Należy zachować dodatkową ostrożność, gdy żądania mogą się przeplatać: istnieje możliwość, że stan ziarna jest obserwowany przed zmianą await instrukcji przez czas zakończenia operacji asynchronicznej i metoda wznawia wykonywanie.

Na przykład następujące ziarno reprezentuje licznik. Został oznaczony Reentrant, co umożliwia przeplatywanie wielu wywołań. Metoda Increment() powinna zwiększać licznik wewnętrzny i zwracać obserwowaną wartość. Jednak ponieważ Increment() treść metody obserwuje stan ziarna przed await punktem i aktualizuje go później, istnieje możliwość, że wiele przeplatania wykonań Increment() może spowodować _value zmniejszenie całkowitej liczby Increment() odebranych wywołań. Jest to błąd spowodowany niewłaściwym użyciem ponownego uruchamiania.

Usunięcie tego problemu ReentrantAttribute wystarczy, aby rozwiązać ten problem.

[Reentrant]
public sealed class CounterGrain : Grain, ICounterGrain
{
    int _value;
    
    /// <summary>
    /// Increments the grain's value and returns the previous value.
    /// </summary>
    public Task<int> Increment()
    {
        // Do not copy this code, it contains an error.
        var currentVal = _value;
        await Task.Delay(TimeSpan.FromMilliseconds(1_000));
        _value = currentVal + 1;
        return currentValue;
    }
}

Aby zapobiec takim błędom, ziarna są domyślnie niezgodne z regułami. Minusem jest zmniejszenie przepływności dla ziarna, które wykonują operacje asynchroniczne w ich implementacji, ponieważ nie można przetworzyć innych żądań, podczas gdy ziarno oczekuje na zakończenie operacji asynchronicznej. Aby to złagodzić, Orleans oferuje kilka opcji umożliwiających ponowne wprowadzenie w niektórych przypadkach:

  • Dla całej klasy: umieszczenie ReentrantAttribute elementu na ziarnie umożliwia dowolne żądanie do ziarna przeplatać się z dowolnym innym żądaniem.
  • W przypadku podzestawu metod: umieszczanie AlwaysInterleaveAttribute metody na interfejsie ziarna pozwala żądać do tej metody przeplatać się z dowolnym innym żądaniem i żądań do tej metody, które mają być przeplatane przez inne żądanie.
  • W przypadku podzestawu metod: umieszczanie ReadOnlyAttribute metody na interfejsie ziarna pozwala żądać do tej metody przeplatać się z dowolnym innym ReadOnly żądaniem i żądań do tej metody, które mają być przeplatane przez inne ReadOnly żądanie. W tym sensie jest to bardziej ograniczona forma AlwaysInterleave.
  • W przypadku dowolnego żądania w łańcuchu wywołań: RequestContext.AllowCallChainReentrancy() i <xref:Orleans. Runtime.RequestContext.SuppressCallChainReentrancy?displayProperty=nameWithType umożliwia wyrażenie zgody na ponowne ponowne wprowadzenie żądań podrzędnych do ziarna i rezygnację z niego. Wywołania zwracają obie wartości, które muszą zostać usunięte podczas zamykania żądania. W związku z tym prawidłowe użycie jest następujące:
public Task<int> OuterCall(IMyGrain other)
{
    // Allow call-chain reentrancy for this grain, for the duration of the method.
    using var _ = RequestContext.AllowCallChainReentrancy();
    await other.CallMeBack(this.AsReference<IMyGrain>());
}

public Task CallMeBack(IMyGrain grain)
{
    // Because OuterCall allowed reentrancy back into that grain, this method 
    // will be able to call grain.InnerCall() without deadlocking.
    await grain.InnerCall();
}

public Task InnerCall() => Task.CompletedTask;

Ponowne wprowadzenie łańcucha wywołań musi być opted-in per-grain, per-call-chain. Rozważmy na przykład dwa ziarna, ziarno A i ziarno B. Jeśli ziarno A włącza ponowne wywołanie łańcucha wywołań przed wywołaniem ziarna B, ziarno B może wywołać z powrotem do ziarna A w tym wywołaniu. Jednak ziarno A nie może wywołać z powrotem do ziarna B, jeśli ziarno B nie włączyło również ponownego wywołania łańcucha wywołań. Jest to per-grain, per-call-chain.

Ziarna mogą również pomijać informacje o ponownym pobieraniu łańcucha wywołań z przepływu w dół łańcucha wywołań przy użyciu polecenia using var _ = RequestContext.SuppressCallChainReentrancy(). Zapobiega to ponownym wywoływaniu kolejnych wywołań.

skrypty migracji ADO.NET

Aby zapewnić zgodność z Orleans klastrowaniem, trwałością i przypomnieniami opartymi na ADO.NET, potrzebny jest odpowiedni skrypt migracji SQL:

Wybierz pliki używanej bazy danych i zastosuj je w kolejności.