Kornpersistenz

Aggregationsintervalle können über mehrere benannte persistente Datenobjekte verfügen. Diese Zustandsobjekte werden während der Kornaktivierung aus dem Speicher geladen, sodass sie während der Anforderungen verfügbar sind. Die Grain-Persistenz verwendet ein erweiterbares Plug-in-Modell, das die Nutzung von Speicher-Providern für jede Datenbank ermöglicht. Dieses Persistenzmodell ist auf Einfachheit ausgelegt und soll nicht alle Datenzugriffsmuster abdecken. Getreide kann auch direkt auf Datenbanken zugreifen, ohne das Kornpersistenzmodell zu verwenden.

Kornpersistenzdiagramm

Im vorherigen Diagramm verfügt UserGrain über einen Profilstatus und einen Cart-Zustand , die jeweils in einem separaten Speichersystem gespeichert sind.

Ziele

  1. Unterstützt mehrere benannte persistente Datenobjekte pro Korn.
  2. Lassen Sie mehrere konfigurierte Speicheranbieter zu, die potenziell eine andere Konfiguration haben und von einem anderen Speichersystem gesichert werden.
  3. Ermöglichen Sie der Community, Speicheranbieter zu entwickeln und zu veröffentlichen.
  4. Geben Sie Speicheranbietern vollständige Kontrolle darüber, wie sie Kornzustandsdaten im permanenten Sicherungsspeicher speichern. Corollary: Orleans bietet keine umfassende ORM-Speicherlösung, ermöglicht aber benutzerdefinierten Speicheranbietern, bestimmte ORM-Anforderungen nach Bedarf zu unterstützen.

Pakete

Sie finden Orleans Getreidespeicheranbieter auf NuGet. Zu den offiziell gepflegten Paketen gehören:

Programmierschnittstelle (API)

Körner interagieren mit ihrem persistenten Zustand, wobei sie IPersistentState<TState>verwenden, bei dem TState der serialisierbare Zustandstyp ist:

public interface IPersistentState<TState> : IStorage<TState>
{
}

public interface IStorage<TState> : IStorage
{
    TState State { get; set; }
}

public interface IStorage
{
    string Etag { get; }

    bool RecordExists { get; }

    Task ClearStateAsync();

    Task WriteStateAsync();

    Task ReadStateAsync();
}
public interface IPersistentState<TState> where TState : new()
{
    TState State { get; set; }

    string Etag { get; }

    Task ClearStateAsync();

    Task WriteStateAsync();

    Task ReadStateAsync();
}

Orleans injiziert Instanzen von IPersistentState<TState> als Konstruktorparameter in das Grain. Sie können diese Parameter mit einem PersistentStateAttribute Attribut kommentieren, um den Namen des injizierten Zustands und den Namen des Speicheranbieters zu identifizieren, der ihn angibt. Im folgenden Beispiel wird dies veranschaulicht, indem zwei benannte Zustände in den UserGrain Konstruktor eingefügt werden:

public class UserGrain : Grain, IUserGrain
{
    private readonly IPersistentState<ProfileState> _profile;
    private readonly IPersistentState<CartState> _cart;

    public UserGrain(
        [PersistentState("profile", "profileStore")] IPersistentState<ProfileState> profile,
        [PersistentState("cart", "cartStore")] IPersistentState<CartState> cart)
    {
        _profile = profile;
        _cart = cart;
    }

    public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);

    public async Task SetNameAsync(string name)
    {
        _profile.State.Name = name;
        await _profile.WriteStateAsync();
    }
}

Verschiedene Getreidetypen können unterschiedliche konfigurierte Speicheranbieter verwenden, auch wenn beide denselben Typ haben (z. B. zwei verschiedene Azure Table Storage-Anbieterinstanzen, die mit verschiedenen Azure Storage-Konten verbunden sind).

Lesestatus

Der Aggregationsintervall-Zustand wird automatisch gelesen, wenn der Aggregationsintervall aktiviert wird, aber Aggregationsintervalle sind dafür verantwortlich, den Schreibvorgang für jeden geänderten Aggregationsintervall-Zustand bei Bedarf explizit auszulösen.

Wenn ein Aggregationsintervall den aktuellen Zustand explizit aus dem Sicherungsspeicher erneut lesen möchte, sollte die ReadStateAsync Methode aufgerufen werden. Dadurch wird der Aggregationsintervallzustand aus dem persistenten Speicher über den Speicheranbieter neu geladen. Die vorherige In-Memory-Kopie des Zustands des Aggregationsintervalls wird überschrieben und ersetzt, wenn der Task von ReadStateAsync abgeschlossen ist.

Greifen Sie mithilfe der State Eigenschaft auf den Wert des Zustands zu. Die folgende Methode greift beispielsweise auf den im Code oben deklarierten Profilstatus zu:

public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);

Während des normalen Betriebs ist es nicht erforderlich, ReadStateAsync aufzurufen; Orleans lädt den Zustand während der Aktivierung automatisch. Sie können jedoch ReadStateAsync verwenden, um den extern geänderten Zustand zu aktualisieren.

Ausführliche Informationen zu Fehlerbehandlungsmechanismen finden Sie unten im Abschnitt " Fehlermodi ".

Schreiben des Zustands

Sie können den Zustand über die State Eigenschaft ändern. Der geänderte Zustand wird nicht automatisch beibehalten. Stattdessen entscheiden Sie, wann der Zustand beibehalten werden soll, indem Sie die WriteStateAsync Methode aufrufen. Mit der folgenden Methode wird beispielsweise eine Eigenschaft auf State aktualisiert und der aktualisierte Zustand gespeichert.

public async Task SetNameAsync(string name)
{
    _profile.State.Name = name;
    await _profile.WriteStateAsync();
}

Konzeptionell nimmt die Orleans Runtime eine vollständige Kopie des Aggregationsintervall-Zustand-Datenobjekts für seine Verwendung bei allen Schreibvorgängen. Im Hintergrund kann die Runtime Optimierungsregeln und Heuristiken verwenden, um unter bestimmten Umständen die Durchführung einiger oder aller vollständigen Kopien zu vermeiden, sofern die erwartete logische Isolationssemantik erhalten bleibt.

Weitere Informationen zu Fehlerbehandlungsmechanismen finden Sie im Abschnitt " Fehlermodi " weiter unten.

Status löschen

Die ClearStateAsync Methode löscht den Lagerzustand des Getreides. Je nach Anbieter kann dieser Vorgang optional den Kornzustand vollständig löschen.

Loslegen

Bevor ein Grain Datenpersistenz verwenden kann, müssen Sie einen Speicheranbieter im Silo konfigurieren.

Konfigurieren Sie zunächst Speicheranbieter, eine für den Profilstatus und eine für den Warenkorbstatus:

Die Verwendung DefaultAzureCredential mit einem URI-Endpunkt ist der empfohlene Ansatz für Produktionsumgebungen.

var tableEndpoint = new Uri(configuration["AZURE_TABLE_STORAGE_ENDPOINT"]!);
var blobEndpoint = new Uri(configuration["AZURE_BLOB_STORAGE_ENDPOINT"]!);
var credential = new DefaultAzureCredential();

var builder = Host.CreateApplicationBuilder();
builder.UseOrleans(siloBuilder =>
{
    siloBuilder.AddAzureTableGrainStorage(
        name: "profileStore",
        configureOptions: options =>
        {
            options.TableServiceClient = new TableServiceClient(tableEndpoint, credential);
        })
        .AddAzureBlobGrainStorage(
            name: "cartStore",
            configureOptions: options =>
            {
                options.BlobServiceClient = new BlobServiceClient(blobEndpoint, credential);
            });
});

using var host = builder.Build();
var host = new HostBuilder()
    .UseOrleans(siloBuilder =>
    {
        siloBuilder.AddAzureTableGrainStorage(
            name: "profileStore",
            configureOptions: options =>
            {
                // Use JSON for serializing the state in storage
                options.UseJson = true;

                // Configure the storage connection key
                options.ConnectionString =
                    "DefaultEndpointsProtocol=https;AccountName=data1;AccountKey=SOMETHING1";
            })
            .AddAzureBlobGrainStorage(
                name: "cartStore",
                configureOptions: options =>
                {
                    // Use JSON for serializing the state in storage
                    options.UseJson = true;

                    // Configure the storage connection key
                    options.ConnectionString =
                        "DefaultEndpointsProtocol=https;AccountName=data2;AccountKey=SOMETHING2";
                });
    })
    .Build();

Von Bedeutung

Microsoft empfiehlt, immer den sichersten Authentifizierungsflow zu verwenden. Wenn Sie eine Verbindung mit Azure SQL herstellen, ist Managed Identities for Azure Resources die empfohlene Authentifizierungsmethode.

Nachdem Sie nun einen Speicheranbieter mit dem Namen "profileStore" konfiguriert haben, können Sie von einem Aggregationsintervall aus auf diesen Anbieter zugreifen.

Sie können einem Getreide auf zwei primäre Weise beständigen Zustand hinzufügen:

  1. Durch Einfügen von IPersistentState<TState> in den Konstruktor des Aggregationsintervalls.
  2. Durch Erben von Grain<TGrainState>.

Die empfohlene Möglichkeit zum Hinzufügen von Speicher zu einem Aggregationsintervall besteht darin, IPersistentState<TState> zu einem zugeordneten [PersistentState("stateName", "providerName")]-Attribut in den Konstruktor des Aggregationsintervalls einzufügen. Einzelheiten zu Grain<TGrainState> finden Sie unten unter Using Grain<TGrainState> to add storage to a grain. Die Verwendung Grain<TGrainState> wird weiterhin unterstützt, gilt jedoch als legacy-Ansatz.

Deklarieren Sie eine Klasse, die den Zustand Ihres Getreides speichert.

[Serializable]
public class ProfileState
{
    public string Name { get; set; }

    public Date DateOfBirth { get; set; }
}

Fügen Sie IPersistentState<TState> in den Konstruktor des Aggregationsintervalls ein:

public class UserGrainSimple : Grain, IUserGrain
{
    private readonly IPersistentState<ProfileState> _profile;

    public UserGrainSimple(
        [PersistentState("profile", "profileStore")]
        IPersistentState<ProfileState> profile)
    {
        _profile = profile;
    }

    public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);

    public async Task SetNameAsync(string name)
    {
        _profile.State.Name = name;
        await _profile.WriteStateAsync();
    }
}

Von Bedeutung

Der Profilstatus wird zu dem Zeitpunkt, zu dem er in den Konstruktor eingefügt wird, nicht geladen, sodass der Zugriff darauf zu diesem Zeitpunkt ungültig ist. Der Zustand wird geladen, bevor OnActivateAsync aufgerufen wird.

Nachdem das Korn nun einen beständigen Zustand aufweist, können Sie Methoden zum Lesen und Schreiben des Zustands hinzufügen:

public class UserGrainComplete : Grain, IUserGrain
{
    private readonly IPersistentState<ProfileState> _profile;

    public UserGrainComplete(
        [PersistentState("profile", "profileStore")]
        IPersistentState<ProfileState> profile)
    {
        _profile = profile;
    }

    public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);

    public async Task SetNameAsync(string name)
    {
        _profile.State.Name = name;
        await _profile.WriteStateAsync();
    }
}

Fehlermodi für Persistenzvorgänge

Fehlermodi für Lesevorgänge

Fehler, die vom Speicheranbieter während des anfänglichen Lesens von Statusdaten für ein bestimmtes Korn zurückgegeben werden, schlagen beim Aktivierungsvorgang für dieses Korn fehl. In solchen Fällen gibt es keinen Aufruf der Lebenszyklusrückrufmethode dieses Aggregationsintervalls OnActivateAsync. Die ursprüngliche Anforderung an das Aggregationsintervall das die Aktivierungsfehler zurück an den Aufrufer verursacht hat, genau wie jeder andere Fehler während der Aktivierung des Aggregationsintervalls. Fehler, die vom Speicheranbieter beim Lesen von Zustandsdaten für ein bestimmtes Aggregationsintervall auftreten, führen zu einer Ausnahme von ReadStateAsyncTask. Der Grain kann wählen, ob er die Task Ausnahme bearbeiten oder ignorieren soll, wie jedes andere Task in Orleans.

Jeder Versuch, eine Nachricht an einen Grain zu senden, der aufgrund einer fehlenden oder fehlerhaften Speicheranbieterkonfiguration beim Silostart nicht geladen werden konnte, führt zu dem permanenten Fehler BadProviderConfigException.

Fehlermodi für Schreibvorgänge

Fehler, die vom Speicheranbieter beim Lesen von Zustandsdaten für ein bestimmtes Aggregationsintervall auftreten, führen zu einer Ausnahme, die von WriteStateAsyncTask ausgelöst wird. In der Regel bedeutet dies, dass die Aggregationsintervall-Aufruf-Ausnahme an den Clientaufrufer zurückgegeben wird, vorausgesetzt, dass WriteStateAsyncTask ordnungsgemäß mit der endgültigen Rückgabe Task für diese Aggregationsintervall-Methode verkettet ist. In bestimmten erweiterten Szenarien ist es jedoch möglich, Aggregationsintervall-Code zu schreiben, um solche Schreibfehler speziell zu behandeln, genau wie alle anderen fehlerhaften Task.

Aggregationsintervalle, die Fehlerbehandlung/Wiederherstellungscode ausführen, müssen Ausnahmen oder fehlerhafte WriteStateAsyncTasks abfangen und nicht erneut auslösen, um anzugeben, dass sie den Schreibfehler erfolgreich behandelt haben.

Empfehlungen

Verwenden der JSON-Serialisierung oder eines anderen versionstoleranten Serialisierungsformats

Code wird weiterentwickelt, und dies umfasst häufig Speichertypen. Um diese Änderungen zu berücksichtigen, konfigurieren Sie einen geeigneten Serialisierer. Orleans Ab 7.0 können Sie den Kornspeicher serialisierer mithilfe der IGrainStorageSerializer Schnittstelle konfigurieren. Standardmäßig serialisiert der Körnungszustand mit JSON (Newtonsoft.Json). Stellen Sie sicher, dass beim Entwickeln von Datenverträgen bereits gespeicherte Daten weiterhin geladen werden können. Weitere Informationen finden Sie unter Grain storage serializers.

Code wird weiterentwickelt, und dies umfasst häufig Speichertypen. Um diese Änderungen zu berücksichtigen, konfigurieren Sie einen geeigneten Serialisierer. Für die meisten Speicheranbieter steht eine UseJson Option oder ein ähnliches Format zur Verwendung von JSON als Serialisierungsformat zur Verfügung. Stellen Sie sicher, dass beim Entwickeln von Datenverträgen bereits gespeicherte Daten weiterhin geladen werden können.

Verwendung von Grain<TGrainState> zum Hinzufügen von Speicher zu einem Grain

Von Bedeutung

Die Verwendung von Grain<TGrainState> zum Hinzufügen von Speicher zu einem Aggregationsintervall gilt als veraltete Funktionalität. Fügen Sie Getreidespeicher wie zuvor beschrieben unter Verwendung von IPersistentState<TState> hinzu.

Grain-Klassen, die von Grain<TGrainState> erben (wobei T ein anwendungsspezifischer Zustandsdatentyp ist, der Persistenz benötigt), laden ihren Zustand automatisch aus dem angegebenen Speicher.

Markieren Sie solche Körner mit einer Angabe einer StorageProviderAttribute benannten Instanz eines Speicheranbieters, die zum Lesen/Schreiben der Statusdaten für dieses Korn verwendet werden soll.

[StorageProvider(ProviderName = "store1")]
public class MyGrain : Grain<MyGrainState>, IMyGrain
{
    public Task DoSomethingAsync() => Task.CompletedTask;
}

Die Grain<TGrainState> Basisklasse definiert die folgenden Methoden für Unterklassen, die aufgerufen werden sollen:

public abstract class GrainBaseExample<TState>
{
    protected virtual Task ReadStateAsync() { return Task.CompletedTask; }
    protected virtual Task WriteStateAsync() { return Task.CompletedTask; }
    protected virtual Task ClearStateAsync() { return Task.CompletedTask; }
}

Das Verhalten dieser Methoden entspricht ihren zuvor definierten Gegenstücken auf IPersistentState<TState>.

Erstellen eines Speicheranbieters

Es gibt zwei Teile der Zustandspersistenz-APIs: die API, die dem Korn über IPersistentState<TState> oder Grain<TGrainState> bereitgestellt wird, und die Speicheranbieter-API, die sich um IGrainStorage zentriert—die Schnittstelle, die Speicheranbieter implementieren müssen.

/// <summary>
/// Interface to be implemented for a storage able to read and write Orleans grain state data.
/// </summary>
public interface ICustomGrainStorage
{
    /// <summary>Read data function for this storage instance.</summary>
    /// <param name="stateName">Name of the state for this grain</param>
    /// <param name="grainId">Grain ID</param>
    /// <param name="grainState">State data object to be populated for this grain.</param>
    /// <typeparam name="T">The grain state type.</typeparam>
    /// <returns>Completion promise for the Read operation on the specified grain.</returns>
    Task ReadStateAsync<T>(
        string stateName, GrainId grainId, IGrainState<T> grainState);

    /// <summary>Write data function for this storage instance.</summary>
    /// <param name="stateName">Name of the state for this grain</param>
    /// <param name="grainId">Grain ID</param>
    /// <param name="grainState">State data object to be written for this grain.</param>
    /// <typeparam name="T">The grain state type.</typeparam>
    /// <returns>Completion promise for the Write operation on the specified grain.</returns>
    Task WriteStateAsync<T>(
        string stateName, GrainId grainId, IGrainState<T> grainState);

    /// <summary>Delete / Clear data function for this storage instance.</summary>
    /// <param name="stateName">Name of the state for this grain</param>
    /// <param name="grainId">Grain ID</param>
    /// <param name="grainState">Copy of last-known state data object for this grain.</param>
    /// <typeparam name="T">The grain state type.</typeparam>
    /// <returns>Completion promise for the Delete operation on the specified grain.</returns>
    Task ClearStateAsync<T>(
        string stateName, GrainId grainId, IGrainState<T> grainState);
}
/// <summary>
/// Interface to be implemented for a storage able to read and write Orleans grain state data.
/// </summary>
public interface IGrainStorage
{
    /// <summary>Read data function for this storage instance.</summary>
    /// <param name="grainType">Type of this grain [fully qualified class name]</param>
    /// <param name="grainReference">Grain reference object for this grain.</param>
    /// <param name="grainState">State data object to be populated for this grain.</param>
    /// <returns>Completion promise for the Read operation on the specified grain.</returns>
    Task ReadStateAsync(
        string grainType, GrainReference grainReference, IGrainState grainState);

    /// <summary>Write data function for this storage instance.</summary>
    /// <param name="grainType">Type of this grain [fully qualified class name]</param>
    /// <param name="grainReference">Grain reference object for this grain.</param>
    /// <param name="grainState">State data object to be written for this grain.</param>
    /// <returns>Completion promise for the Write operation on the specified grain.</returns>
    Task WriteStateAsync(
        string grainType, GrainReference grainReference, IGrainState grainState);

    /// <summary>Delete / Clear data function for this storage instance.</summary>
    /// <param name="grainType">Type of this grain [fully qualified class name]</param>
    /// <param name="grainReference">Grain reference object for this grain.</param>
    /// <param name="grainState">Copy of last-known state data object for this grain.</param>
    /// <returns>Completion promise for the Delete operation on the specified grain.</returns>
    Task ClearStateAsync(
        string grainType, GrainReference grainReference, IGrainState grainState);
}

Erstellen Sie einen benutzerdefinierten Speicheranbieter, indem Sie diese Schnittstelle implementieren und diese Implementierung registrieren . Ein Beispiel für eine vorhandene Speicheranbieterimplementierung finden Sie unter AzureBlobGrainStorage.

Speicheranbietersemantik

Ein undurchsichtiger anbieterspezifischer Etag Wert (string) kann von einem Speicheranbieter als Teil der Kornstatusmetadaten festgelegt werden, die beim Lesen des Zustands aufgefüllt wurden. Einige Anbieter können dies als null belassen, wenn sie Etag nicht verwenden.

Jeder Versuch, einen Schreibvorgang auszuführen, wenn der Speicheranbieter einen EtagVerstoß gegen eine Einschränkung feststellt, sollte dazu führen, dass der SchreibvorgangTask mit einem vorübergehenden Fehler InconsistentStateException und einem Umbruch der zugrunde liegenden Speicherausnahme abgebrochen wird.

public class InconsistentStateException : OrleansException
{
    public InconsistentStateException(
    string message,
    string storedEtag,
    string currentEtag,
    Exception storageException)
        : base(message, storageException)
    {
        StoredEtag = storedEtag;
        CurrentEtag = currentEtag;
    }

    public InconsistentStateException(
        string storedEtag,
        string currentEtag,
        Exception storageException)
        : this(storageException.Message, storedEtag, currentEtag, storageException)
    {
    }

    /// <summary>The Etag value currently held in persistent storage.</summary>
    public string StoredEtag { get; }

    /// <summary>The Etag value currently held in memory, and attempting to be updated.</summary>
    public string CurrentEtag { get; }
}

Alle anderen Fehlerbedingungen eines Speichervorgangs müssen dazu führen, dass der zurückgegebene Task durch eine Ausnahme aufgelöst wird, die das zugrunde liegende Speicherproblem angibt. In vielen Fällen könnte diese Ausnahme an den Aufrufer zurückgegeben werden, der den Speichervorgang durch Aufrufen einer Methode für den Aggregationsintervall ausgelöst hat. Es ist wichtig zu überlegen, ob der Aufrufer diese Ausnahme deserialisieren kann. Beispielsweise hat der Client möglicherweise nicht die spezifische Persistenzbibliothek geladen, die den Ausnahmetyp enthält. Aus diesem Grund ist es ratsam, Ausnahmen in Ausnahmen umzuwandeln, die an den Aufrufer zurückverbreitet werden können.

Datenzuordnung

Einzelne Speicheranbieter sollten entscheiden, wie der Kornzustand am besten gespeichert werden soll – Blob (verschiedene Formate/ serialisierte Formulare) oder Spalten pro Feld sind offensichtliche Auswahlmöglichkeiten.

Registrieren eines Speicheranbieters

Die Orleans Laufzeit löst einen Speicheranbieter vom Dienstanbieter (IServiceProvider) auf, wenn ein Aggregationsintervall erstellt wird. Die Laufzeit löst eine Instanz von IGrainStorage. Wenn der Speicheranbieter benannt ist (z. B. über das [PersistentState(stateName, storageName)] Attribut), wird eine benannte Instanz IGrainStorage aufgelöst.

Um eine benannte Instanz von IGrainStoragezu registrieren, verwenden Sie die AddSingletonNamedService Erweiterungsmethode nach dem Beispiel des AzureTableGrainStorage-Anbieters hier.

Redis-Aggregationsintervallpersistenz

Redis ist ein beliebter In-Memory-Datenspeicher, der für die Persistenz von Körnern verwendet werden kann. Das Paket Microsoft.Orleans.Persistence.Redis bietet einen Grain-Speicheranbieter, der durch Redis unterstützt wird.

Konfigurieren der Redis-Getreidespeicherung

Konfigurieren Sie den Redis-Getreidespeicher mithilfe der AddRedisGrainStorage Erweiterungsmethode:

var builder = Host.CreateApplicationBuilder();
builder.UseOrleans(siloBuilder =>
{
    siloBuilder.AddRedisGrainStorage(
        name: "redis",
        configureOptions: options =>
        {
            options.ConfigurationOptions = new ConfigurationOptions
            {
                EndPoints = { "localhost:6379" },
                AbortOnConnectFail = false
            };
        });
});

using var host = builder.Build();

Verwenden Sie AddRedisGrainStorageAsDefaultFolgendes, um Redis als Standardspeicheranbieter für Getreide zu konfigurieren:

siloBuilder.AddRedisGrainStorageAsDefault(options =>
{
    options.ConfigurationOptions = new ConfigurationOptions
    {
        EndPoints = { "localhost:6379" }
    };
});

Redis-Speicheroptionen

Die RedisStorageOptions Klasse bietet die folgenden Konfigurationsoptionen:

Eigentum Typ Description
ConfigurationOptions ConfigurationOptions Die StackExchange.Redis-Clientkonfiguration. Erforderlich.
DeleteStateOnClear bool Gibt an, ob der Status bei ClearStateAsync Aufruf von Redis gelöscht werden soll. Der Standardwert ist false.
EntryExpiry TimeSpan? Optionale Ablaufzeit für Einträge. Legen Sie dies nur für kurzlebige Umgebungen wie Tests fest, da sie doppelte Aktivierungen verursachen kann. Der Standardwert ist null.
GrainStorageSerializer IGrainStorageSerializer Der Serialisierer, der für den Kornzustand verwendet werden soll. Der Standardwert verwendet den Orleans Serialisierer.
CreateMultiplexer Func<RedisStorageOptions, Task<IConnectionMultiplexer>> Benutzerdefinierte Factory zur Erstellung eines Redis-Verbindungsmultiplexers.
GetStorageKey Func<string, GrainId, RedisKey> Benutzerdefinierte Funktion zum Generieren des Redis-Schlüssels für ein Korn. Das Standardformat ist {ServiceId}/state/{grainId}/{grainType}.

.NET Aspire Integration

Bei Verwendung von .NET Aspire können Sie den Redis Grain-Speicher in die von Aspire verwaltete Redis-Ressource integrieren.

// In your AppHost project
var redis = builder.AddRedis("orleans-redis");

var orleans = builder.AddOrleans("cluster")
    .WithGrainStorage("Default", redis);

builder.AddProject<Projects.OrleansServer>("silo")
    .WithReference(orleans)
    .WaitFor(redis);
// Register the Redis client with keyed services.
// Orleans providers look up resources by their keyed service name.
// builder.AddKeyedRedisClient("orleans-redis");

builder.UseOrleans(siloBuilder =>
{
    siloBuilder.AddRedisGrainStorage(
        name: "redis",
        configureOptions: options =>
        {
            // Use the Aspire-provided connection string
            var connectionString = builder.Configuration.GetConnectionString("orleans-redis");
            options.ConfigurationOptions = ConfigurationOptions.Parse(connectionString!);
        });
});

Für komplexere Szenarien können Sie die IConnectionMultiplexer direkt mithilfe des CreateMultiplexer Delegaten einfügen:

// Register the Redis client with keyed services.
// Orleans providers look up resources by their keyed service name.
// builder.AddKeyedRedisClient("orleans-redis");

siloBuilder.AddRedisGrainStorage("redis");
siloBuilder.Services.AddOptions<Orleans.Persistence.RedisStorageOptions>("redis")
    .Configure<IServiceProvider>((options, sp) =>
    {
        options.CreateMultiplexer = _ =>
        {
            // Resolve the IConnectionMultiplexer from DI (provided by Aspire)
            return Task.FromResult(sp.GetRequiredService<IConnectionMultiplexer>());
        };
    });

Azure Cosmos DB-Aggregationsintervallpersistenz

Azure Cosmos DB ist eine vollständig verwaltete und relationale NoSQL-Datenbank für die moderne App-Entwicklung. Das Microsoft.Orleans.Persistence.Cosmos Paket bietet einen Getreidespeicheranbieter, der von Cosmos DB unterstützt wird.

Konfigurieren des Cosmos DB-Getreidespeichers

Installieren Sie Microsoft.Orleans. Persistenz.Cosmos NuGet-Paket:

dotnet add package Microsoft.Orleans.Persistence.Cosmos

Konfigurieren Sie Cosmos DB Grain Storage mit der AddCosmosGrainStorage Erweiterungsmethode:

var builder = Host.CreateApplicationBuilder();
builder.UseOrleans(siloBuilder =>
{
    siloBuilder.AddCosmosGrainStorage(
        name: "cosmos",
        configureOptions: options =>
        {
            options.ConfigureCosmosClient(
                "https://myaccount.documents.azure.com:443/",
                new DefaultAzureCredential());
            options.DatabaseName = "Orleans";
            options.ContainerName = "OrleansStorage";
            options.IsResourceCreationEnabled = true;
        });
});

Verwenden Sie AddCosmosGrainStorageAsDefaultfolgendes, um Cosmos DB als Standardspeicheranbieter für Getreide zu konfigurieren:

siloBuilder.AddCosmosGrainStorageAsDefault(options =>
{
    options.ConfigureCosmosClient(
        "https://myaccount.documents.azure.com:443/",
        new DefaultAzureCredential());
    options.IsResourceCreationEnabled = true;
});

Cosmos DB Speicheroptionen

Die CosmosGrainStorageOptions Klasse bietet die folgenden Konfigurationsoptionen:

Eigentum Typ Standard Description
DatabaseName string "Orleans" Der Name der Cosmos DB-Datenbank.
ContainerName string "OrleansStorage" Der Name des Containers für Kornzustandsdaten.
IsResourceCreationEnabled bool false Wenn true, werden die Datenbank und der Container automatisch erstellt, wenn sie noch nicht vorhanden sind.
DeleteStateOnClear bool false Gibt an, ob der Zustand von Cosmos DB gelöscht werden soll, wenn ClearStateAsync aufgerufen wird.
InitStage int ServiceLifecycleStage.ApplicationServices Die Phase des Silolebenszyklus, wenn die Speicherung initialisiert wird.
StateFieldsToIndex List<string> Leer JSON-Pfade von Zustandseigenschaften, die in den Cosmos DB-Index aufgenommen werden sollen.
PartitionKeyPath string "/PartitionKey" Der JSON-Pfad für den Partitionsschlüssel im Container.
DatabaseThroughput int? null Der bereitgestellte Durchsatz für die Datenbank. Wenn null, verwendet den serverlosen Modus.
ContainerThroughputProperties ThroughputProperties? null Die Durchsatzeigenschaften für den Container.
ClientOptions CosmosClientOptions new() Die An den Cosmos DB-Client übergebenen Optionen.

Benutzerdefinierter Partitionsschlüsselanbieter

Verwendet standardmäßig Orleans die Aggregationsintervall-D als Partitionsschlüssel. Für erweiterte Szenarien können Sie einen benutzerdefinierten Partitionsschlüsselanbieter implementieren:

public class MyPartitionKeyProvider : IPartitionKeyProvider
{
    public ValueTask<string> GetPartitionKey(string grainType, GrainId grainId)
    {
        // Custom logic to determine partition key
        return new ValueTask<string>(grainId.Key.ToString()!);
    }
}

Registrieren Sie den benutzerdefinierten Partitionsschlüsselanbieter:

// Register with custom partition key provider
siloBuilder.AddCosmosGrainStorage<MyPartitionKeyProvider>(
    name: "cosmos",
    configureOptions: options =>
    {
        options.ConfigureCosmosClient("https://myaccount.documents.azure.com:443/", new DefaultAzureCredential());
    });