粒紋參考

在對粒紋呼叫方法之前,您必須先參考該粒紋。 粒紋參考是 Proxy 物件,可實作與對應粒紋類別相同的粒紋介面。 其會封裝目標粒紋的邏輯身分識別 (型別和唯一索引鍵)。 粒紋參考用於對目標粒紋進行呼叫。 每個粒紋參考都是單一粒紋 (粒紋類別的單一執行個體),但使用者可以建立相同粒紋的多個獨立參考。

由於粒紋參考代表目標粒紋的邏輯身分識別,其與粒紋的實際位置無關,即使在系統完全重新開機之後仍有效。 開發人員可以使用類似任何其他 .NET 物件的粒紋參考。 可以將其傳遞至方法、用做為方法傳回值,甚至是儲存至持續性儲存體。

將粒紋的身分識別傳遞至 IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) 方法 (其中 T 是粒紋介面),而 key 是型別內粒紋的唯一索引鍵,即可取得粒紋參考。

以下是如何取得上述 IPlayerGrain 介面的粒紋參考範例。

從粒紋類別內:

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

從 Orleans 用戶端程式碼:

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

粒紋參考包含三項資訊:

  1. 可唯一識別粒紋類別的粒紋型別
  2. 粒紋索引鍵,可唯一識別該粒紋類別的邏輯執行個體。
  3. 粒紋參考必須實作的介面

注意

粒紋型別和索引鍵會形成粒紋身分識別

請注意,上述對於 IGrainFactory.GetGrain 的呼叫僅在這三種情況中的兩個才會接受:

  • 由粒紋參考 IPlayerGrain 所實作的介面
  • 粒紋索引鍵,這是 playerId 的值。

儘管指出粒紋參考包含粒紋型別、索引鍵和介面,但範例只提供 Orleans 索引鍵和介面。 這是因為 Orleans 會維護粒紋介面與粒紋型別之間的對應。 當您要求粒紋處理站 IShoppingCartGrain 時,Orleans 會參考其對應以尋找對應的粒紋型別,以便建立參考。 這適用於只有一個粒紋介面的實作,但如果有多個實作,則必須在 GetGrain 呼叫中加以區分。 如需詳細資訊,請參閱下一節,區分粒紋型別解析

注意

Orleans 會在編譯期間,為應用程式中的每個粒紋介面產生粒紋參考實作型別。 這些粒紋參考實作繼承自 Orleans.Runtime.GrainReference 類別。 GetGrain 會傳回對應至所要求粒紋介面之所產生 Orleans.Runtime.GrainReference 實作的執行個體。

區分粒紋型別解析

當粒紋介面有多個實作時,例如在下列範例中,Orleans 會嘗試在建立粒紋參考時判斷預期的實作。 請考慮下列範例,其中 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
}

下列對於 GetGrain 的呼叫會擲回例外狀況,因為 Orleans 不知道如何將 ICounterGrain 明確對應至其中一個粒紋類別。

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

System.ArgumentException 會擲回下列訊息:

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

錯誤訊息會告訴您哪些粒紋實作的 Orleans 符合要求的粒紋介面型別 ICounterGrain。 它會顯示粒紋型別名稱 (upcounterdowncounter) 以及粒紋類別 (UpCounterGrainDownCounterGrain)。

注意

錯誤訊息 upcounterdowncounter 中的粒紋型別名稱分別衍生自粒紋類別名稱 UpCounterGrainDownCounterGrain。 這是 Orleans 中的預設行為,其可藉由將 [GrainType(string)] 屬性新增至粒紋類別來自訂。 例如:

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

有數種方式可以解決下列小節中詳述的這種語意模糊。

使用唯一標記介面區分粒紋型別

區分這些粒紋的最清楚方式,就是提供它們獨特的粒紋介面。 例如,如果我們將介面 IUpCounterGrain 新增至 UpCounterGrain 類別,並將 IDownCounterGrain 介面新增至 DownCounterGrain 類別,如下列範例所示,然後我們可以傳遞 IUpCounterGrainIDownCounterGrainGetGrain<T> 呼叫來解析正確的粒紋參考,而不是傳遞語意模糊的 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
}

若要建立任一粒紋的參考,請考慮下列程式碼:

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

注意

在上述範例中,您已使用相同的索引鍵,但不同的粒紋型別建立兩個粒紋參考。 第一個儲存在 myUpCounter 變數中,即識別碼為 upcounter/my-counter 之粒紋的參考。 第二個儲存在 myDownCounter 變數中,即識別碼為 downcounter/my-counter 之粒紋的參考。 這是可唯一識別粒紋的粒紋型別和粒紋索引鍵組合。 因此,myUpCountermyDownCounter 會參考不同的粒紋。

藉由提供粒紋類別前置詞來區分粒紋型別

您可以將粒紋類別名稱前置詞提供給 IGrainFactory.GetGrain,例如:

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

使用命名慣例指定預設粒紋實作

在區分相同粒紋介面的多個實作時,Orleans 將會使用從介面名稱去除前置「I」的慣例來選取實作。 例如,如果介面名稱是 ICounterGrain,且有兩個實作 CounterGrainDownCounterGrain,則 Orleans 會在要求 ICounterGrain 的參考時選擇 CounterGrain,如下列範例所示:

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

使用屬性指定預設粒紋型別

Orleans.Metadata.DefaultGrainTypeAttribute 屬性可以新增至粒紋介面,以指定該介面之預設實作的粒紋型別,如下列範例所示:

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

藉由提供解析的粒紋識別碼來區分粒紋型別

某些 IGrainFactory.GetGrain 的多載接受類型 Orleans.Runtime.GrainId 的引數。 使用這些多載時,Orleans 不需要從介面型別對應到粒紋型別,因此不會有語意模糊的解析。 例如:

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