.NET geliştirici kılavuzunda dayanıklı varlıklar

Bu kılavuz, örnekler ve genel öneriler de dahil olmak üzere dayanıklı varlıkları .NET ile tanımlamayı, bunlara erişmeyi ve yönetmeyi kapsar.

Varlık işlevleri, uygulama durumunu sunucusuz bir ortamda ayrıntılı varlıklar koleksiyonu olarak düzenlemenize olanak tanır. Temel alınan kavramlar için bkz. Dayanıklı varlıklar: Kavramlar.

Bu makalede:

Söz diziminizi seçin

Varlıkları tanımlamak için iki API kullanılabilir:

  • Sınıf tabanlı söz dizimi, varlıkları ve işlemleri sınıflar ve yöntemler olarak temsil eder. Bu söz dizimi okunabilir kod oluşturur ve işlemlerin arabirimler aracılığıyla tür denetimi yapılmış bir şekilde çağrılmasını sağlar. Çoğu uygulama için önerilir.

  • İşlev tabanlı söz dizimi , varlıkları işlev olarak temsil eden alt düzey bir arabirimdir. Varlık işlemlerinin nasıl iletildiği ve varlık durumunun nasıl yönetildiği konusunda üstün kontrol sağlar. Özel soyutlamalara veya kitaplık düzeyinde genelliğe ihtiyacınız olduğunda bunu kullanın.

Note

Sınıf tabanlı söz dizimi, işlev tabanlı söz diziminin üzerindeki bir katmandır. Her iki değişken de aynı uygulamada birbirinin yerine kullanılabilir.

Varlık sınıflarını tanımlama

Aşağıdaki örnek, tamsayı türünde tek bir değeri depolayan ve dört işlem Counter( , AddResetGetve ) sunan bir Delete varlığın uygulamasıdır.

Important

.NET işlem içi modeli bakım modundadır. Yeni projeler için .NET yalıtılmış çalışan modelini kullanın (bu sayfanın en üstteki sekmesini seçin). Yönergeler için bkz. Yalıtılmış çalışan modeline geçiş .

[JsonObject(MemberSerialization.OptIn)]
public class Counter
{
    [JsonProperty("value")]
    public int Value { get; set; }

    public void Add(int amount) 
    {
        this.Value += amount;
    }

    public Task Reset() 
    {
        this.Value = 0;
        return Task.CompletedTask;
    }

    public Task<int> Get() 
    {
        return Task.FromResult(this.Value);
    }

    public void Delete() 
    {
        Entity.Current.DeleteState();
    }

    [FunctionName(nameof(Counter))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
        => ctx.DispatchAsync<Counter>();
}

Run işlevi, sınıf tabanlı söz dizimini kullanmak için gerekli şablon kodu içerir. static Azure İşlevi olmalıdır. Varlık tarafından işlenen her işlem mesajı için bir kez yürütülür. Çağrıldığında DispatchAsync<T> ve varlık henüz bellekte bulunmuyorsa, bir T türünde nesne oluşturur ve alanlarını depolamada bulunan son saklanan JSON'dan doldurur (varsa). Ardından ismi eşleşen yöntemi çağırır.

Bu örnekteki EntityTrigger İşlevin Run Entity sınıfının içinde bulunması gerekmez. Azure İşlevi için geçerli herhangi bir konumda bulunabilir: üst düzey ad alanının içinde veya üst düzey bir sınıfın içinde. Ancak, daha derin iç içe yerleştirilmişse (örneğin, Fonksiyon iç içe bir sınıf içinde bildirilmişse), bu Fonksiyon en son çalışma zamanı tarafından tanınmaz.

Note

Sınıf tabanlı varlığın durumu, varlık bir işlemi işlemeden önce örtük olarak oluşturulur ve çağrılarak bir işlemde Entity.Current.DeleteState().

Note

Yalıtılmış modelde varlıkları çalıştırmak için Azure İşlevleri Core Tools sürüm 4.0.5455 veya üzeri gerekir.

C# yalıtılmış çalışan modelinde bir varlığı sınıf olarak tanımlamanın iki yolu vardır. Farklı durum serileştirme yapılarına sahip varlıklar üretirler.

Aşağıdaki yaklaşımla, bir varlık tanımlanırken nesnenin tamamı serileştirilir.

public class Counter
{
    public int Value { get; set; }

    public void Add(int amount) 
    {
        this.Value += amount;
    }

    public Task Reset() 
    {
        this.Value = 0;
        return Task.CompletedTask;
    }

    public Task<int> Get() 
    {
        return Task.FromResult(this.Value);
    }

    // Delete is implicitly defined when defining an entity this way

    [Function(nameof(Counter))]
    public static Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
        => dispatcher.DispatchAsync<Counter>();
}

TaskEntity<TState> tabanlı bir uygulama, bağımlılık enjeksiyonunun kullanımını kolaylaştırır. Bu durumda, durum State özelliğine seri durumdan çıkarılır ve başka hiçbir özellik serileştirilip/seri durumdan çıkarılmaz.

public class Counter : TaskEntity<int>
{
    readonly ILogger logger; 

    public Counter(ILogger<Counter> logger)
    {
        this.logger = logger; 
    }

    public void Add(int amount) 
    {
        this.State += amount;
    }

    public Task Reset() 
    {
        this.State = 0;
        return Task.CompletedTask;
    }

    public Task<int> Get() 
    {
        return Task.FromResult(this.State);
    }

    // Delete is implicitly defined when defining an entity this way

    [Function(nameof(Counter))]
    public static Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
        => dispatcher.DispatchAsync<Counter>();
}

Warning

Varlıklar ITaskEntity veya TaskEntity<TState>'den türetildiğinde, varlık tetikleyici yönteminizi olarak RunAsync önemlidir. Bu, varlık çağrılırken, ITaskEntity zaten bir örnek düzeyi "RunAsync" tanımladığından, "RunAsync" yöntem adıyla belirsiz bir eşleşme olduğu için çalışma zamanı hatalarına neden olur.

Yalıtılmış modeldeki varlıkları silme

Yalıtılmış modeldeki bir varlığın silinmesi, varlık durumu nullolarak ayarlanarak gerçekleştirilir ve bu işlem kullanılan varlık uygulama yoluna bağlıdır.

ITaskEntity veya işlev tabanlı söz dizimi ile silme

ITaskEntity türetilirken veya işlev tabanlı söz dizimi kullanılarak, silme işlemi TaskEntityOperation.State.SetState(null) çağrılarak gerçekleştirilir:

// Inside a function-based entity dispatch
switch (operation.Name.ToLowerInvariant())
{
    case "delete":
        operation.State.SetState(null);
        break;
}

TaskEntity<TState kullanarak sil>

TaskEntity<TState> türetilirken, silme işlemi örtük şekilde tanımlanır. Ancak, varlıkta bir yöntem Delete tanımlanarak geçersiz kılınabilir. Durum, this.State = null aracılığıyla herhangi bir işlemden de silinebilir.

  • null durumunu TState null yapılabilir hale getirerek silmek için.
  • Örtülü olarak tanımlanan silme işlemi, null değer kabul etmeyen TState öğesini siler.

Aşağıdaki örnekte, varsayılan silme işlemini geçersiz kılan null atanabilir durumu olan bir TaskEntity<int?> gösterilmektedir.

public class Counter : TaskEntity<int?>
{
    public void Delete()
    {
        // Custom logic before deleting, such as logging
        this.State = null;
    }

    [Function(nameof(Counter))]
    public static Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
        => dispatcher.DispatchAsync<Counter>();
}

POCO varlığıyla sil

Durum olarak bir POCO kullanırken (TaskEntity<TState>'dan türetilmezse), bir silme işlemi dolaylı olarak tanımlanır. POCO'da bir yöntem Delete tanımlayarak silme işlemini geçersiz kılmak mümkündür. Ancak POCO yolunda durumu null olarak ayarlamanın bir yolu yoktur, bu nedenle örtük olarak tanımlanan silme işlemi tek doğru silme işlemidir.

public class Counter
{
    public int Value { get; set; }

    // The implicit delete operation handles state removal.
    // Defining a Delete method here overrides the implicit behavior,
    // but you cannot set state to null from within a POCO entity.

    [Function(nameof(Counter))]
    public static Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
        => dispatcher.DispatchAsync<Counter>();
}

Varlık sınıfı gereksinimleri

Varlık sınıfları özel üst sınıflar, arabirimler veya öznitelikler gerektirmeyen POCO'lardır (düz eski CLR nesneleri). Ancak:

İşlem olarak çağrılan herhangi bir yöntem şu kuralları karşılamalıdır:

  • Bir işlemin en fazla bir bağımsız değişkeni olmalıdır ve aşırı yüklemeler veya genel tür bağımsız değişkenleri olmamalıdır.
  • Bir arabirim kullanılarak bir orkestrasyondan çağrılması gereken bir işlem, Task veya Task<T> döndürmelidir.
  • Bağımsız değişkenler ve dönüş değerleri serileştirilebilen değerler veya nesneler olmalıdır.

İşlemler ne yapabilir?

Tüm varlık işlemleri varlık durumunu okuyup güncelleştirebilir ve durumdaki değişiklikler otomatik olarak depolamada kalıcı olur. Ayrıca, işlemler Azure İşlevleri'ın genel sınırlamaları dahilinde harici girdi/çıktı veya diğer hesaplamaları gerçekleştirebilir.

İşlemler bağlam tarafından Entity.Current sağlanan işlevlere de erişebilir:

  • EntityName: şu anda yürütülmekte olan işlem biriminin adı.
  • EntityKey: şu anda yürütülmekte olan varlığın anahtarı.
  • EntityId: şu anda yürütülmekte olan varlığın kimliği (ad ve anahtar içerir).
  • SignalEntity: bir varlığa tek yönlü ileti gönderir.
  • CreateNewOrchestration: yeni bir düzenleme başlatır.
  • DeleteState: bu varlığın durumunu siler.

Örneğin, sayaç 100'e ulaştığında ve varlık kimliğini giriş bağımsız değişkeni olarak geçirdiğinde düzenleme başlatabilmesi için sayaç varlığını değiştirebiliriz:

public void Add(int amount) 
{
    if (this.Value < 100 && this.Value + amount >= 100)
    {
        Entity.Current.StartNewOrchestration("MilestoneReached", Entity.Current.EntityId);
    }
    this.Value += amount;      
}

Varlıklara doğrudan erişme

Sınıf tabanlı varlıklara, varlık ve işlemleri için açık dize adları kullanılarak doğrudan erişilebilir. Bu bölümde örnekler verilmiştir. Temel kavramlar (sinyaller ve çağrılar gibi) hakkında daha ayrıntılı bir açıklama için Access varlıklarındaki tartışmaya bakın.

Note

Mümkün olduğunda, daha fazla tür denetimi sağladığından varlıklara arabirimler aracılığıyla erişmeniz gerekir.

Örnek: istemci sinyalleri birimi

Aşağıdaki Azure Http İşlevi, REST kurallarını kullanarak bir DELETE işlemi uygular. URL yoluna anahtarı geçirilen sayaç varlığına bir silme sinyali gönderilir.

[FunctionName("DeleteCounter")]
public static async Task<HttpResponseMessage> DeleteCounter(
    [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "Counter/{entityKey}")] HttpRequestMessage req,
    [DurableClient] IDurableEntityClient client,
    string entityKey)
{
    var entityId = new EntityId("Counter", entityKey);
    await client.SignalEntityAsync(entityId, "Delete");    
    return req.CreateResponse(HttpStatusCode.Accepted);
}

Örnek: istemci varlık durumunu okur

Aşağıdaki Azure HTTP İşlevi, REST kurallarını kullanarak bir GET işlemi uygular. Anahtarı URL yolunda geçirilen sayaç varlığının geçerli durumunu okur.

[FunctionName("GetCounter")]
public static async Task<HttpResponseMessage> GetCounter(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "Counter/{entityKey}")] HttpRequestMessage req,
    [DurableClient] IDurableEntityClient client,
    string entityKey)
{
    var entityId = new EntityId("Counter", entityKey);
    var state = await client.ReadEntityStateAsync<Counter>(entityId); 
    return req.CreateResponse(state);
}

Note

tarafından ReadEntityStateAsync döndürülen nesne yalnızca yerel bir kopyadır, yani zamanın önceki bir noktasından varlık durumunun anlık görüntüsüdür. Özellikle eski olabilir ve bu nesnenin değiştirilmesinin gerçek varlık üzerinde hiçbir etkisi yoktur.

Örnek: Orchestration önce sinyal gönderir, sonra varlığı çağırır

Aşağıdaki düzenleme, bir sayaç varlığını artırması için sinyal gönderir ve ardından en son değerini okumak için aynı varlığı çağırır.

[FunctionName("IncrementThenGet")]
public static async Task<int> Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var entityId = new EntityId("Counter", "myCounter");

    // One-way signal to the entity - does not await a response
    context.SignalEntity(entityId, "Add", 1);

    // Two-way call to the entity which returns a value - awaits the response
    int currentValue = await context.CallEntityAsync<int>(entityId, "Get");

    return currentValue;
}

Örnek: istemci sinyalleri birimi

Aşağıdaki Azure HTTP İşlevi, REST kurallarını kullanarak bir DELETE işlemi uygular. URL yoluna anahtarı geçirilen sayaç varlığına bir silme sinyali gönderilir.

[Function("DeleteCounter")]
public static async Task<HttpResponseData> DeleteCounter(
    [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "Counter/{entityKey}")] HttpRequestData req,
    [DurableClient] DurableTaskClient client, string entityKey)
{
    var entityId = new EntityInstanceId("Counter", entityKey);
    await client.Entities.SignalEntityAsync(entityId, "Delete");
    return req.CreateResponse(HttpStatusCode.Accepted);
}

Örnek: istemci varlık durumunu okur

Aşağıdaki Azure HTTP İşlevi, REST kurallarını kullanarak bir GET işlemi uygular. Anahtarı URL yolunda geçirilen sayaç varlığının geçerli durumunu okur.

[Function("GetCounter")]
public static async Task<HttpResponseData> GetCounter(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "Counter/{entityKey}")] HttpRequestData req,
    [DurableClient] DurableTaskClient client, string entityKey)
{
    var entityId = new EntityInstanceId("Counter", entityKey);
    EntityMetadata<int>? entity = await client.Entities.GetEntityAsync<int>(entityId);
    HttpResponseData response = request.CreateResponse(HttpStatusCode.OK);
    await response.WriteAsJsonAsync(entity.State);

    return response;
}

Örnek: Orchestration önce sinyal gönderir, sonra varlığı çağırır

Aşağıdaki düzenleme, bir sayaç varlığını artırması için sinyal gönderir ve ardından en son değerini okumak için aynı varlığı çağırır.

[Function("IncrementThenGet")]
public static async Task<int> Run([OrchestrationTrigger] TaskOrchestrationContext context)
{
    var entityId = new EntityInstanceId("Counter", "myCounter");

    // One-way signal to the entity - does not await a response
    await context.Entities.SignalEntityAsync(entityId, "Add", 1);

    // Two-way call to the entity which returns a value - awaits the response
    int currentValue = await context.Entities.CallEntityAsync<int>(entityId, "Get");

    return currentValue; 
}

Arabirimler aracılığıyla varlıklara erişme

Arabirimler, oluşturulan ara sunucu nesneleri aracılığıyla varlıklara erişmek için kullanılabilir. Bu yaklaşım, bir işlemin adının ve bağımsız değişken türünün uygulananla eşleşmesini sağlar. Varlıklara erişmek için mümkün olduğunda arabirimlerin kullanılması önerilir.

Örneğin, sayaç örneğini değiştirebiliriz:

public interface ICounter
{
    void Add(int amount);
    Task Reset();
    Task<int> Get();
    void Delete();
}

public class Counter : ICounter
{
    ...
}

Varlık sınıfları ve varlık arabirimleri, Orleans tarafından popülerleştirilen taneciklere ve tane arabirimlerine benzer. Dayanıklı Varlıklar ile Orleans arasındaki benzerlikler ve farklar hakkında daha fazla bilgi için bkz . Sanal aktörlerle karşılaştırma.

Arayüzler, tür denetimi sağlamanın yanı sıra, uygulama içindeki kaygıların daha iyi ayrıştırılması için kullanışlıdır. Örneğin, bir varlık birden çok arabirim uygulayabildiğinden, tek bir varlık birden çok rol sağlayabilir. Ayrıca, birden çok varlık bir arabirim uygulayabildiğinden, genel iletişim desenleri yeniden kullanılabilir kitaplıklar olarak uygulanabilir.

Örnek: istemci arabirim aracılığıyla bileşene sinyal gönderir.

İstemci kodu, SignalEntityAsync<TEntityInterface>'i uygulayan varlıklara sinyal göndermek için TEntityInterface kullanabilir. Örneğin:

[FunctionName("DeleteCounter")]
public static async Task<HttpResponseMessage> DeleteCounter(
    [HttpTrigger(AuthorizationLevel.Function, "delete", Route = "Counter/{entityKey}")] HttpRequestMessage req,
    [DurableClient] IDurableEntityClient client,
    string entityKey)
{
    var entityId = new EntityId("Counter", entityKey);
    await client.SignalEntityAsync<ICounter>(entityId, proxy => proxy.Delete());    
    return req.CreateResponse(HttpStatusCode.Accepted);
}

Bu örnekte, proxy parametresi, ICounter'nin dinamik olarak oluşturulmuş bir örneğidir ve bu örnek, Delete çağrısını dahili olarak bir sinyale dönüştürür.

Note

API'ler SignalEntityAsync yalnızca tek yönlü işlemler için kullanılabilir. Eğer bir işlem Task<T> döndürürse bile, T parametresinin değeri her zaman null veya default olur, gerçek sonuç yerine. Örneğin, bir değer döndürmediğinden işlemin sinyalini Get vermek mantıklı değildir. Bunun yerine, istemciler sayaç durumuna doğrudan erişim için ReadStateAsync kullanabilir veya Get işlemini çağıran bir orchestrator işlevi başlatabilir.

Örnek: Orkestrasyon önce sinyal verir, ardından vekil sunucu aracılığıyla varlığı çağırır.

Bir düzenlemenin içinden bir varlığı çağırmak veya sinyal göndermek için, CreateEntityProxy arabirim türüyle birlikte bu varlık için bir vekil oluşturmak üzere kullanılabilir. Bu ara sunucu daha sonra çağrı veya sinyal işlemleri için kullanılabilir:

[FunctionName("IncrementThenGet")]
public static async Task<int> Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var entityId = new EntityId("Counter", "myCounter");
    var proxy = context.CreateEntityProxy<ICounter>(entityId);

    // One-way signal to the entity - does not await a response
    proxy.Add(1);

    // Two-way call to the entity which returns a value - awaits the response
    int currentValue = await proxy.Get();

    return currentValue;
}

Örtük olarak, void döndüren tüm işlemler işaretlenir ve Task veya Task<T> döndüren tüm işlemler çağrılır. Varsayılan davranışı değiştirmek ve görev döndürseler bile sinyal işlemlerini gerçekleştirmek, SignalEntity<IInterfaceType> yöntemini açıkça kullanarak mümkündür.

Yalnızca anahtara göre varlık hedefi belirtme

Arabirim kullanarak bir varlığı çağırırken veya sinyal gönderirken, ilk bağımsız değişkenin hedef varlığı belirtmesi gerekir. Varlığı uygulayan tek bir sınıf olduğunda varlık kimliği veya yalnızca varlık anahtarı tanımlanarak hedef belirtilebilir:

context.SignalEntity<ICounter>(new EntityId(nameof(Counter), "myCounter"), ...);
context.SignalEntity<ICounter>("myCounter", ...);

Yalnızca varlık anahtarı belirtilirse ve çalışma zamanında benzersiz bir uygulama bulunamazsa, InvalidOperationException oluşturulur.

Varlık arabirimleri üzerindeki kısıtlamalar

Her zamanki gibi, tüm parametre ve dönüş türleri JSON serileştirilebilir olmalıdır. Aksi takdirde, serileştirme istisnaları çalışma zamanında oluşturulur. Aşağıdaki kurallar da geçerlidir:

  • Varlık arabirimleri, varlık sınıfıyla aynı derlemede tanımlanmalıdır.
  • Varlık arabirimleri yalnızca yöntemleri tanımlamalıdır.
  • Varlık arabirimleri genel parametreler içermemelidir.
  • Varlık arabirimi yöntemlerinin birden fazla parametresi olmamalıdır.
  • Varlık arabirimi yöntemleri , voidveya TaskdöndürmelidirTask<T>.

Bu kurallardan herhangi biri ihlal edilirse, arabirim InvalidOperationException, SignalEntity veya SignalEntityAsync için tip argümanı olarak kullanıldığında çalışma zamanında bir CreateEntityProxy fırlatılır. Özel durum iletisinde hangi kuralın bozuk olduğu açıklanır.

Note

Döndürülen void arabirim yöntemleri yalnızca işaretlenebilir (tek yönlü), çağrılmaz (iki yönlü). Dönen Task veya Task<T> değerlerine sahip arabirim yöntemleri ya çağrılabilir ya da sinyallendirilebilir. Çağrılırsa, işlemin sonucunu döndürür veya işlem tarafından oluşan özel durumları yeniden oluşturur. İşaretliyse, işlemden elde edilen gerçek sonucu veya özel durumu değil varsayılan değeri döndürür.

Arabirim tabanlı varlık erişimi şu anda yalıtılmış .NET çalışanda desteklenmemaktadır. Bunun yerine doğrudan dize tabanlı erişim kullanın. Özellik isteğini Azure İşlevleri Durable extension deposunda izleyin.

Varlık serileştirme

Bir varlığın durumu kalıcı olarak kalıcı olduğundan, varlık sınıfının serileştirilebilir olması gerekir. Dayanıklı İşlevler çalışma zamanı bu amaçla Json.NET kitaplığını kullanır ve bu kitaplık serileştirme ve seri durumdan çıkarma işlemini denetlemeye yönelik ilkeleri ve öznitelikleri destekler. En yaygın kullanılan C# veri türleri (diziler ve koleksiyon türleri dahil) zaten serileştirilebilir ve dayanıklı varlıkların durumunu tanımlamak için kolayca kullanılabilir.

Örneğin, Json.NET aşağıdaki sınıfı kolayca seri hale getirip seri durumdan çıkarabilir:

[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class User
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("yearOfBirth")]
    public int YearOfBirth { get; set; }

    [JsonProperty("timestamp")]
    public DateTime Timestamp { get; set; }

    [JsonProperty("contacts")]
    public Dictionary<Guid, Contact> Contacts { get; set; } = new Dictionary<Guid, Contact>();

    [JsonObject(MemberSerialization = MemberSerialization.OptOut)]
    public struct Contact
    {
        public string Name;
        public string Number;
    }

    ...
}

Serileştirme Öznitelikleri

Yukarıdaki örnekte, temel serileştirmeyi daha görünür hale getirmek için birkaç öznitelik ekleyeceğiz:

  • Sınıfı serileştirilebilir olması gerektiğini hatırlatmak ve yalnızca açıkça JSON özellikleri olarak işaretlenmiş üyeleri kalıcı hale getirmek için [JsonObject(MemberSerialization.OptIn)] ile açıklama yaparız.
  • Bir alanın kalıcı [JsonProperty("name")] varlık durumunun bir parçası olduğunu anımsatmak ve JSON gösteriminde kullanılacak özellik adını belirtmek için kalıcı olacak alanlara açıklama ekleriz.

Ancak, bu öznitelikler gerekli değildir; Json.NET ile çalıştıkları sürece diğer kurallar veya özniteliklere izin verilir. Örneğin, [DataContract] öznitelikleri kullanabilir veya hiç öznitelik kullanmaya bilir.

[DataContract]
public class Counter
{
    [DataMember]
    public int Value { get; set; }
    ...
}

public class Counter
{
    public int Value;
    ...
}

Varsayılan olarak, sınıfın adı* JSON gösteriminin bir parçası olarak depolanmaz; yani varsayılan ayar olarak kullanırız TypeNameHandling.None . Bu varsayılan davranış, JsonObject ve JsonProperty öznitelikleri kullanılarak geçersiz kılınabilir.

Sınıf tanımlarında değişiklik yapma

Depolanan JSON nesnesi artık yeni sınıf tanımıyla eşleşmediğinden, uygulama çalıştırıldıktan sonra sınıf tanımında değişiklik yaparken bazı özen gösterilmesi gerekir. Bir kişinin JsonConvert.PopulateObject tarafından kullanılan seri durumdan çıkarma sürecini anladığı sürece değişen veri biçimleriyle doğru bir şekilde başa çıkmak genellikle mümkündür. Değişikliklere ve bunların etkisine örnekler aşağıda verilmiştir:

  • Depolanan JSON'da mevcut olmayan yeni bir özellik eklendiğinde, varsayılan değerini varsayar.
  • Depolanan JSON'da bulunan bir özellik kaldırıldığında, önceki içerik kaybolur.
  • Bir özellik yeniden adlandırıldığında, bu, eskisinin kaldırılıp yenisinin eklenmesi etkisini yaratır.
  • Özellik türü, depolanmış JSON'dan seri durumdan çıkarılamayacak şekilde değiştirildiğinde bir istisna fırlatılır.
  • Özellik türü depolanmış JSON'dan seri durumdan çıkartılabilir halde kalması için değiştirildiğinde, bunu yapar.

Json.NET davranışını özelleştirmek için kullanılabilecek birçok seçenek vardır. Örneğin, depolanan JSON sınıfında bulunmayan bir alan içeriyorsa özel durumu zorlamak için özniteliğini JsonObject(MissingMemberHandling = MissingMemberHandling.Error)belirtin. İsteğe bağlı biçimlerde depolanan JSON'ı okuyabilen seri durumdan çıkarma için özel kod yazmak da mümkündür.

Yalıtılmış çalışan modeli, varsayılan olarak System.Text.Json yerine Newtonsoft.Json kullanır. Bu, varlık durumunun nasıl serileştirildiğini ve seriden çıkarıldığını değiştirir. Önemli farklar şunlardır:

  • Özellikler varsayılan olarak büyük/küçük harfe duyarlıdır (oysa Newtonsoft.Json içinde büyük/küçük harfe duyarsızdır).
  • [JsonProperty] öznitelikleri Newtonsoft.Json tanınmıyor. [JsonPropertyName] kullanın System.Text.Json.Serialization'dan.
  • Dictionary<TKey, TValue> serileştirme varsayılan olarak camelCase anahtarlarını kullanır.

Serileştirme ve deserileştirme davranışını özelleştirme hakkında ayrıntılı yönergeler için bkz. Serileştirme ve deserileştirmeyi özelleştirme.

Varlık oluşturma

Bazen varlık nesnelerinin nasıl oluşturulurken daha fazla denetim sahibi olmak istiyoruz. Şimdi varlık nesneleri oluştururken varsayılan davranışı değiştirmek için çeşitli seçenekleri açıklıyoruz.

İlk erişimde özel başlatma

Bazen, hiç erişilmemiş veya silinmiş bir varlığa bir işlem göndermeden önce bazı özel başlatmalar yapmamız gerekir. Bu davranışı belirtmek için, önce DispatchAsyncbir koşullu ekleyebilirsiniz:

[FunctionName(nameof(Counter))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
{
    if (!ctx.HasState)
    {
        ctx.SetState(...);
    }
    return ctx.DispatchAsync<Counter>();
}

Varlık sınıflarındaki bağlamalar

Normal işlevlerden farklı olarak, varlık sınıfı yöntemlerinin giriş ve çıkış bağlamalarına doğrudan erişimi yoktur. Bunun yerine, bağlama verisi giriş noktası işlevi bildiriminde yakalanmalı ve ardından DispatchAsync<T> yöntemine geçirilmelidir. DispatchAsync<T>'ye geçirilen herhangi bir nesne, otomatik olarak varlık sınıfı oluşturucusuna bağımsız değişken olarak aktarılır.

Aşağıdaki örnek, blob giriş bağlamasından bir CloudBlobContainer başvurunun sınıf tabanlı bir varlık için nasıl kullanılabilir hale getirilebileceğini gösterir.

public class BlobBackedEntity
{
    [JsonIgnore]
    private readonly CloudBlobContainer container;

    public BlobBackedEntity(CloudBlobContainer container)
    {
        this.container = container;
    }

    // ... entity methods can use this.container in their implementations ...

    [FunctionName(nameof(BlobBackedEntity))]
    public static Task Run(
        [EntityTrigger] IDurableEntityContext context,
        [Blob("my-container", FileAccess.Read)] CloudBlobContainer container)
    {
        // passing the binding object as a parameter makes it available to the
        // entity class constructor
        return context.DispatchAsync<BlobBackedEntity>(container);
    }
}

Azure İşlevleri'ndeki bağlamalar hakkında daha fazla bilgi için bkz. Azure İşlevleri tetikleyicileri ve bağlamaları kavramları.

Varlık sınıflarında bağımlılık ekleme

Varlık sınıfları Azure İşlevleri Bağımlılık Ekleme destekler. Aşağıdaki örnekte, bir IHttpClientFactory hizmetin sınıf tabanlı bir varlığa nasıl kaydedilecekleri gösterilmektedir.

[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]

namespace MyNamespace
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddHttpClient();
        }
    }
}

Aşağıdaki kod parçacığı, eklenen hizmetin varlık sınıfınıza nasıl eklendiğini gösterir:

public class HttpEntity
{
    [JsonIgnore]
    private readonly HttpClient client;

    public HttpEntity(IHttpClientFactory factory)
    {
        this.client = factory.CreateClient();
    }

    public Task<int> GetAsync(string url)
    {
        using (var response = await this.client.GetAsync(url))
        {
            return (int)response.StatusCode;
        }
    }

    [FunctionName(nameof(HttpEntity))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
        => ctx.DispatchAsync<HttpEntity>();
}

İlk erişimde özel başlatma

public class Counter : TaskEntity<int>
{
    protected override int InitializeState(TaskEntityOperation operation)
    {
        // This is called when state is null, giving a chance to customize first-access of entity.
        return 10;
    }
}

Varlık sınıflarındaki bağlamalar

Aşağıdaki örnekte, sınıf tabanlı bir varlıkta blob giriş bağlamasının nasıl kullanılacağı gösterilmektedir.

public class BlobBackedEntity : TaskEntity<object?>
{
    private BlobContainerClient Container { get; set; }

    [Function(nameof(BlobBackedEntity))]
    public Task DispatchAsync(
        [EntityTrigger] TaskEntityDispatcher dispatcher, 
        [BlobInput("my-container")] BlobContainerClient container)
    {
        this.Container = container;
        return dispatcher.DispatchAsync(this);
    }
}

Azure İşlevleri bağlamaları hakkında daha fazla bilgi için Azure İşlevleri Tetikleyicileri ve Bağlamaları belgelerine bakın.

Varlık sınıflarında bağımlılık ekleme

Varlık sınıfları Azure İşlevleri Bağımlılık Ekleme destekler.

Aşağıdaki örnek, daha sonra varlık sınıfına içeri aktarılacak şekilde HttpClient'nin program.cs dosyasında nasıl yapılandırılacağını göstermektedir.

public class Program
{
    public static void Main()
    {
        IHost host = new HostBuilder()
            .ConfigureFunctionsWorkerDefaults((IFunctionsWorkerApplicationBuilder workerApplication) =>
            {
                workerApplication.Services.AddHttpClient<HttpEntity>()
                    .ConfigureHttpClient(client => {/* configure http client here */});
             })
            .Build();

        host.Run();
    }
}

Aşağıdaki örnekte eklenen hizmetin varlık sınıfınıza nasıl ekleneceği gösterilmektedir:

public class HttpEntity : TaskEntity<object?>
{
    private readonly HttpClient client;

     public HttpEntity(HttpClient client)
    {
        this.client = client;
    }

    public async Task<int> GetAsync(string url)
    {
        using var response = await this.client.GetAsync(url);
        return (int)response.StatusCode;
    }

    [Function(nameof(HttpEntity))]
    public static Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
        => dispatcher.DispatchAsync<HttpEntity>();
}

Note

Serileştirmeyle ilgili sorunları önlemek için eklenen değerleri depolayan alanları serileştirmenin dışında tutduğunuzdan emin olun.

Note

Normal .NET Azure İşlevleri'da yapıcı enjeksiyonu kullanmaktan farklı olarak, sınıf tabanlı varlıklar için işlevin giriş noktası yöntemi muststatic olarak ilan edilmelidir. Statik olmayan bir işlev giriş noktası bildirmek, normal Azure İşlevleri nesne başlatıcısı ile Dayanıklı Varlıklar nesne başlatıcısı arasında çakışmalara neden olabilir.

İşlev tabanlı söz dizimi

Şimdiye kadar bu kılavuz, çoğu uygulama için önerilen sınıf tabanlı söz dizimine odaklanmaktadır. İşlev tabanlı söz dizimi, işlem gönderimi ve durum yönetimi üzerinde doğrudan denetim sağlar. Özel soyutlamalara veya kitaplık düzeyinde genelliğe ihtiyacınız olduğunda bunu kullanın.

İşlev tabanlı söz dizimi ile Entity İşlevi, işlem gönderimini açıkça işler ve varlığın durumunu açıkça yönetir. Örneğin aşağıdaki kod, işlev tabanlı söz dizimi kullanılarak uygulanan Counter varlığını gösterir.

[FunctionName("Counter")]
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
{
    switch (ctx.OperationName.ToLowerInvariant())
    {
        case "add":
            ctx.SetState(ctx.GetState<int>() + ctx.GetInput<int>());
            break;
        case "reset":
            ctx.SetState(0);
            break;
        case "get":
            ctx.Return(ctx.GetState<int>());
            break;
        case "delete":
            ctx.DeleteState();
            break;
    }
}

Varlık bağlam nesnesi

Varlığa özgü işlevlere türündeki IDurableEntityContextbir bağlam nesnesi aracılığıyla erişilebilir. Bu bağlam nesnesi, varlık işlevi için bir parametre olarak ve async-local özelliği Entity.Currentaracılığıyla kullanılabilir.

Aşağıdaki üyeler geçerli işlem hakkında bilgi sağlar ve bir dönüş değeri belirtmeye yardımcı olur:

  • EntityName: Şu anda yürütülmekte olan varlığın adı
  • EntityKey: Şu anda yürütülmekte olan varlığın anahtarı
  • EntityId: Şu anda yürütülmekte olan varlığın kimliği (ad ve anahtar içerir)
  • OperationName: Geçerli işlemin adı
  • GetInput<TInput>(): İşlem için geçerli girişi alır
  • Return(arg): İşi çağıran orkestrasyona bir değer döndürür

Aşağıdaki üyeler varlığın durumunu yönetir (oluşturma, okuma, güncelleştirme, silme):

  • HasState: Varlık varsa; yani, bazı durumlara sahipse
  • GetState<TState>(): Varlığın geçerli durumunu alır ve oluşmamışsa bir tane oluşturur
  • SetState(arg): Varlığın durumunu oluşturur veya güncelleştirir
  • DeleteState(): Varsa varlığın durumunu siler

tarafından GetState döndürülen durum bir nesneyse, uygulama kodu bunu değiştirebilir. Sonunda yeniden aramaya SetState gerek yoktur (ancak bir sakıncası da yoktur). Birden çok kez çağrılırsa GetState<TState> , aynı tür kullanılmalıdır.

Son olarak, aşağıdaki üyeler diğer varlıklara sinyal gönderir veya yeni düzenleme başlatır:

  • SignalEntity(EntityId, operation, input): Varlığa tek yönlü ileti gönderir
  • CreateNewOrchestration(orchestratorFunctionName, input): Yeni bir düzenleme başlatır
[Function(nameof(Counter))]
public static Task DispatchAsync([EntityTrigger] TaskEntityDispatcher dispatcher)
{
    return dispatcher.DispatchAsync(operation =>
    {
        if (operation.State.GetState(typeof(int)) is null)
        {
            operation.State.SetState(0);
        }

        switch (operation.Name.ToLowerInvariant())
        {
            case "add":
                int state = operation.State.GetState<int>();
                state += operation.GetInput<int>();
                operation.State.SetState(state);
                return new(state);
            case "reset":
                operation.State.SetState(0);
                break;
            case "get":
                return new(operation.State.GetState<int>());
            case "delete": 
                operation.State.SetState(null);
                break; 
        }

        return default;
    });
}

Sonraki Adımlar