'da serileştirme özelleştirmesi Orleans

Önemli yönlerinden Orleans biri, bir nesneyi veya veri yapısını depolanabilen veya iletilebilen ve daha sonra yeniden oluşturulabilen bir biçime dönüştürme işlemi olan serileştirme özelleştirme desteğidir. Bu, geliştiricilerin verilerin sistemin farklı bölümleri arasında gönderildiğinde nasıl kodlanıp çözüleceklerini denetlemesine olanak tanır. Serileştirme özelleştirmesi performansı, birlikte çalışabilirliği ve güvenliği iyileştirmek için yararlı olabilir.

Serileştirme sağlayıcıları

Orleans iki serileştirici uygulaması sağlar:

Bu paketlerden birini yapılandırmak için bkz . içinde OrleansSerileştirme yapılandırması.

Özel seri hale getirici uygulaması

Özel seri hale getirici uygulaması oluşturmak için birkaç yaygın adım söz konusu olur. Birkaç arabirim uygulamanız ve ardından seri hale getiricinizi çalışma zamanına Orleans kaydetmeniz gerekir. Aşağıdaki bölümlerde adımlar daha ayrıntılı olarak açıklanmaktadır.

Aşağıdaki Orleans serileştirme arabirimlerini uygulayarak başlayın:

  • IGeneralizedCodec: Birden çok türü destekleyen bir codec bileşeni.
  • IGeneralizedCopier: Birden çok türdeki nesneleri kopyalamak için işlevsellik sağlar.
  • ITypeFilter: Türlerin yüklenmesine izin verme ve serileştirme ile seri durumdan çıkarma işlemlerine katılma işlevselliği.

Özel seri hale getirici uygulamasının aşağıdaki örneğini göz önünde bulundurun:

internal sealed class CustomOrleansSerializer :
    IGeneralizedCodec, IGeneralizedCopier, ITypeFilter
{
    void IFieldCodec.WriteField<TBufferWriter>(
        ref Writer<TBufferWriter> writer, 
        uint fieldIdDelta,
        Type expectedType,
        object value) =>
        throw new NotImplementedException();

    object IFieldCodec.ReadValue<TInput>(
        ref Reader<TInput> reader, Field field) =>
        throw new NotImplementedException();

    bool IGeneralizedCodec.IsSupportedType(Type type) =>
        throw new NotImplementedException();

    object IDeepCopier.DeepCopy(object input, CopyContext context) =>
        throw new NotImplementedException();

    bool IGeneralizedCopier.IsSupportedType(Type type) =>
        throw new NotImplementedException();
}

Yukarıdaki örnek uygulamada:

  • Her arabirim, yöntem adı çözümlemesiyle çakışmaları önlemek için açıkça uygulanır.
  • Her yöntem, yönteminin uygulanmadığını belirtmek için bir NotImplementedException oluşturur. İstenen işlevselliği sağlamak için her yöntemi uygulamanız gerekir.

Sonraki adım, seri hale getiricinizi çalışma zamanına Orleans kaydetmektir. Bu genellikle özel AddCustomSerializer bir uzantı yöntemini genişleterek ISerializerBuilder ve kullanıma sunarak elde edilir. Aşağıdaki örnekte tipik desen gösterilmektedir:

using Microsoft.Extensions.DependencyInjection;
using Orleans.Serialization;
using Orleans.Serialization.Serializers;
using Orleans.Serialization.Cloning;

public static class SerializationHostingExtensions
{
    public static ISerializerBuilder AddCustomSerializer(
        this ISerializerBuilder builder)
    {
        var services = builder.Services;

        services.AddSingleton<CustomOrleansSerializer>();
        services.AddSingleton<IGeneralizedCodec, CustomOrleansSerializer>();
        services.AddSingleton<IGeneralizedCopier, CustomOrleansSerializer>();
        services.AddSingleton<ITypeFilter, CustomOrleansSerializer>();

        return builder;
    }
}

Dikkat edilmesi gereken ek noktalar, özel uygulamanıza özgü özel serileştirme seçeneklerini kabul eden bir aşırı yüklemeyi kullanıma sunmanızdır. Bu seçenekler oluşturucudaki kayıtla birlikte yapılandırılabilir. Bu seçenekler, özel seri hale getirici uygulamanıza bağımlılık eklenmiş olabilir.

Orleans sağlayıcı modeli kullanarak üçüncü taraf seri hale getiricilerle tümleştirmeyi destekler. Bu, bu makalenin IExternalSerializer özel serileştirme bölümünde açıklanan türün bir uygulamasını gerektirir. Bazı yaygın serileştiriciler için tümleştirmeler, ile birlikte Orleanskorunur, örneğin:

uygulamasının IExternalSerializer özel uygulaması aşağıdaki bölümde açıklanmıştır.

Özel dış serileştiriciler

Otomatik serileştirme oluşturma işlemine ek olarak, uygulama kodu seçtiği türler için özel serileştirme sağlayabilir. Orleans uygulama türlerinizin çoğu için otomatik serileştirme oluşturmanın kullanılmasını ve seri hale getiricileri el ile kodlayarak performansı geliştirmenin mümkün olduğuna inandığınız nadir durumlarda yalnızca özel seri hale getiriciler yazmanızı önerir. Bu not, bunun nasıl yapılacağını açıklar ve yararlı olabileceği bazı belirli durumları tanımlar.

Uygulamaların serileştirmeyi özelleştirmenin üç yolu vardır:

  1. Türüne serileştirme yöntemleri ekleyin ve bunları uygun özniteliklerle (CopierMethodAttribute, SerializerMethodAttribute, DeserializerMethodAttribute) işaretleyin. Bu yöntem, uygulamanızın sahip olduğu türler, yani yeni yöntemler ekleyebileceğiniz türler için tercih edilir.
  2. Yapılandırma sırasında uygulayın IExternalSerializer ve kaydedin. Bu yöntem, bir dış serileştirme kitaplığını tümleştirmek için kullanışlıdır.
  3. içinde 3 serileştirme yöntemi ve yukarıdaki özniteliklerle birlikte ile [Serializer(typeof(YourType))] ek açıklama ekli ayrı bir statik sınıf yazın. Bu yöntem, uygulamanın sahip olmadığı türler için kullanışlıdır; örneğin, uygulamanızın üzerinde denetimi olmayan diğer kitaplıklarda tanımlanan türler.

Bu serileştirme yöntemlerinin her biri aşağıdaki bölümlerde ayrıntılı olarak anlatılır.

Özel serileştirmeye giriş

Orleans serileştirme üç aşamada gerçekleşir:

  • Yalıtım sağlamak için nesneler hemen derine kopyalanır.
  • Kabloya yerleştirilmeden önce nesneler bir ileti bayt akışına serileştirilir.
  • Hedef etkinleştirmeye teslim edildiğinde, alınan bayt akışından nesneler yeniden oluşturulur (seri durumdan çıkarılır).

İletilerde gönderilebilen veri türleri (yöntem bağımsız değişkenleri veya dönüş değerleri olarak geçirilebilen türler) bu üç adımı gerçekleştiren ilişkili yordamlara sahip olmalıdır. Bu yordamları bir veri türü için seri hale getiriciler olarak toplu olarak adlandırıyoruz.

Seri hale getirici ve seri durumdan çıkarıcı birlikte çalışan bir çiftken, bir tür için kopyalayıcı tek başına durur. Yalnızca özel bir kopyalayıcı veya yalnızca özel seri hale getirici ve özel seri durumdan çıkarıcı sağlayabilir veya üçünün de özel uygulamalarını sağlayabilirsiniz.

Serileştiriciler silo başlatma sırasında ve bir derleme yüklendiğinde desteklenen her veri türü için kaydedilir. Kullanılacak bir tür için özel seri hale getirme yordamları için kayıt gereklidir. Seri hale getirici seçimi, kopyalanacak veya seri hale getirilecek nesnenin dinamik türünü temel alır. Bu nedenle, soyut sınıflar veya arabirimler için seri hale getiriciler oluşturmaya gerek yoktur, çünkü bunlar hiçbir zaman kullanılmayacaktır.

Özel seri hale getirici ne zaman yazılır?

El ile hazırlanmış seri hale getirici yordamı nadiren oluşturulan sürümlerden daha iyi performans gösterir. Bir tane yazmak isterseniz, önce aşağıdaki seçenekleri göz önünde bulundurmanız gerekir:

  • Veri türlerinizde seri hale getirmesi veya kopyalanması gerekmeyen alanlar veya özellikler varsa, bunları ile NonSerializedAttributeişaretleyebilirsiniz. Bu, oluşturulan kodun kopyalama ve serileştirme sırasında bu alanları atlamasına neden olur. Sabit verileri kopyalamaktan kaçınmak için ve Immutable<T> seçeneğini kullanınImmutableAttribute. Daha fazla bilgi için bkz . Kopyalamayı iyileştirme. Standart genel koleksiyon türlerini kullanmaktan kaçınıyorsanız, bunu yapmayın. Çalışma Orleans zamanı, kopyalamayı, serileştirmeyi ve seri durumdan kaldırmayı iyileştirmek için koleksiyonların semantiğini kullanan genel koleksiyonlar için özel serileştiriciler içerir. Bu koleksiyonların serileştirilmiş bayt akışında özel "kısaltılmış" gösterimleri de vardır ve bu da daha fazla performans avantajı sağlar. Örneğin, bir Dictionary<string, string> değerinden List<Tuple<string, string>>daha hızlı olacaktır.

  • Özel seri hale getiricinin fark edilebilir bir performans kazancı sağlayabildiği en yaygın durum, veri türünde kodlanmış önemli anlamsal bilgilerin yalnızca alan değerlerini kopyalayarak kullanılamamasıdır. Örneğin, seyrek olarak doldurulan diziler, uygulama verileri işlem hızı için tam olarak gerçekleştirilen bir dizi olarak tutsa bile diziyi dizin/değer çiftleri koleksiyonu olarak ele alarak genellikle daha verimli bir şekilde seri hale getirilebilir.

  • Özel seri hale getirici yazmadan önce yapmanız gereken önemli bir şey, oluşturulan seri hale getiricinin performansınıza zarar vermesini sağlamaktır. Profil oluşturma burada biraz yardımcı olacaktır, ancak daha da değerli olan, serileştirmenin mikro etkisi yerine sistem düzeyinde etkiyi ölçmek için çeşitli serileştirme yükleriyle uygulamanızın uçtan uca stres testlerini çalıştırmaktır. Örneğin, yalnızca her iki uçta da konserve değerleri kullanarak hiçbir parametre geçirmeden veya bu yöntemlerden sonuç elde etmeyen bir test sürümü oluşturmak, serileştirme ve kopyalamanın sistem performansı üzerindeki etkisini yakınlaştıracaktır.

Türe serileştirme yöntemleri ekleme

Tüm serileştirici yordamları üzerinde çalıştıkları sınıfın veya yapının statik üyeleri olarak uygulanmalıdır. Burada gösterilen adlar gerekli değildir; kayıt, yöntem adlarına değil ilgili özniteliklerin varlığına bağlıdır. Seri hale getirici yöntemlerinin genel olması gerekmediğini unutmayın.

Üç serileştirme yordamını da uygulamadığınız sürece, eksik yöntemlerin sizin için oluşturulması için türünüzü ile SerializableAttribute işaretlemeniz gerekir.

Fotokopi makinesi

Kopyalayıcı yöntemleri ile Orleans.CodeGeneration.CopierMethodAttributeişaretlenir:

[CopierMethod]
static private object Copy(object input, ICopyContext context)
{
    // ...
}

Kopyalayıcılar genellikle yazacak en basit seri hale getirici yordamlarıdır. Kopyalayıcının tanımlandığı türle aynı türde olması garanti edilen bir nesne alır ve nesnenin eşanlamlı olarak eşdeğer bir kopyasını döndürmesi gerekir.

Nesneyi kopyalamanın bir parçası olarak bir alt nesnenin kopyalanması gerekiyorsa, bunu yapmak için en iyi yol yordamı kullanmaktır SerializationManager.DeepCopyInner :

var fooCopy = SerializationManager.DeepCopyInner(foo, context);

Önemli

Tam kopyalama işlemi için nesne kimliği bağlamını korumak için yerine SerializationManager.DeepCopykullanmak SerializationManager.DeepCopyInnerönemlidir.

Nesne kimliğini koruma

Kopyalama yordamının önemli bir sorumluluğu nesne kimliğini korumaktır. Çalışma Orleans zamanı bu amaç için bir yardımcı sınıf sağlar. Bir alt nesneyi "elle" kopyalamadan önce (çağırarak DeepCopyInnerdeğil), aşağıdaki gibi zaten başvurulup başvurulmadığını denetleyin:

var fooCopy = context.CheckObjectWhileCopying(foo);
if (fooCopy is null)
{
    // Actually make a copy of foo
    context.RecordObject(foo, fooCopy);
}

Son satır, başvurularla aynı nesneye RecordObjectyapılan olası başvuruların tarafından CheckObjectWhileCopyingdüzgün bir şekilde foo bulunabilmesi için gerekli olan çağrısıdır.

Not

Bu yalnızca sınıf örnekleri için yapılmalıdır; örnekler veya , Urive enumgibi string.NET temel bilgileri için yapılmamalıdırstruct.

Alt nesneleri kopyalamak için kullanırsanız DeepCopyInner nesne kimliği sizin için işlenir.

Serileştirici

Serileştirme yöntemleri ile Orleans.CodeGeneration.SerializerMethodAttributeişaretlenir:

[SerializerMethod]
static private void Serialize(
    object input,
    ISerializationContext context,
    Type expected)
{
    // ...
}

Fotokopi makinelerde olduğu gibi, seri hale getiriciye geçirilen "input" nesnesinin tanımlama türünün bir örneği olması garanti edilir. "Beklenen" tür yoksayılabilir; veri öğesi hakkındaki derleme zamanı türü bilgilerini temel alır ve bayt akışında tür ön ekini oluşturmak için daha yüksek bir düzeyde kullanılır.

Alt nesneleri seri hale getirmek için yordamını SerializationManager.SerializeInner kullanın:

SerializationManager.SerializeInner(foo, context, typeof(FooType));

Foo için belirli bir beklenen tür yoksa, beklenen tür için null geçirebilirsiniz.

sınıfı, BinaryTokenStreamWriter bayt akışına veri yazmak için çok çeşitli yöntemler sağlar. sınıfının bir örneği özelliği aracılığıyla context.StreamWriter elde edilebilir. Belgeler için sınıfına bakın.

Seri Durumdan Çıkarıcı

Seri durumdan çıkarma yöntemleri ile Orleans.CodeGeneration.DeserializerMethodAttributeişaretlenir:

[DeserializerMethod]
static private object Deserialize(
    Type expected,
    IDeserializationContext context)
{
    //...
}

"Beklenen" tür yoksayılabilir; veri öğesi hakkındaki derleme zamanı türü bilgilerini temel alır ve bayt akışında tür ön ekini oluşturmak için daha yüksek bir düzeyde kullanılır. Oluşturulacak nesnenin gerçek türü her zaman seri durumdan çıkarıcının tanımlandığı sınıf türü olacaktır.

Alt nesneleri seri durumdan çıkarmak için yordamını SerializationManager.DeserializeInner kullanın:

var foo = SerializationManager.DeserializeInner(typeof(FooType), context);

Veya alternatif olarak:

var foo = SerializationManager.DeserializeInner<FooType>(context);

Foo için belirli bir beklenen tür yoksa, genel DeserializeInner olmayan değişkenini kullanın ve beklenen tür için geçirin null .

sınıfı, BinaryTokenStreamReader bayt akışından verileri okumak için çok çeşitli yöntemler sağlar. sınıfının bir örneği özelliği aracılığıyla context.StreamReader elde edilebilir. Belgeler için sınıfına bakın.

Seri hale getirici sağlayıcısı yazma

Bu yöntemde bunu uygular Orleans.Serialization.IExternalSerializer ve hem istemcide GlobalConfiguration hem de ClientConfiguration silolarda özelliğine eklersinizSerializationProviderOptions.SerializationProviders. Yapılandırma hakkında bilgi için bkz . Serileştirme sağlayıcıları.

uygulamaları IExternalSerializer , seri hale getiricinin belirli bir türü destekleyip desteklemediğini belirlemek için kullanılan bir yöntem ve bir Initialize IsSupportedType yöntemin Orleans eklenmesiyle serileştirme için daha önce açıklanan deseni izler. Bu, arabirim tanımıdır:

public interface IExternalSerializer
{
    /// <summary>
    /// Initializes the external serializer. Called once when the serialization manager creates
    /// an instance of this type
    /// </summary>
    void Initialize(Logger logger);

    /// <summary>
    /// Informs the serialization manager whether this serializer supports the type for serialization.
    /// </summary>
    /// <param name="itemType">The type of the item to be serialized</param>
    /// <returns>A value indicating whether the item can be serialized.</returns>
    bool IsSupportedType(Type itemType);

    /// <summary>
    /// Tries to create a copy of source.
    /// </summary>
    /// <param name="source">The item to create a copy of</param>
    /// <param name="context">The context in which the object is being copied.</param>
    /// <returns>The copy</returns>
    object DeepCopy(object source, ICopyContext context);

    /// <summary>
    /// Tries to serialize an item.
    /// </summary>
    /// <param name="item">The instance of the object being serialized</param>
    /// <param name="context">The context in which the object is being serialized.</param>
    /// <param name="expectedType">The type that the deserializer will expect</param>
    void Serialize(object item, ISerializationContext context, Type expectedType);

    /// <summary>
    /// Tries to deserialize an item.
    /// </summary>
    /// <param name="context">The context in which the object is being deserialized.</param>
    /// <param name="expectedType">The type that should be deserialized</param>
    /// <returns>The deserialized object</returns>
    object Deserialize(Type expectedType, IDeserializationContext context);
}

Tek bir tür için seri hale getirici yazma

Bu yöntemde, bir özniteliğiyle [SerializerAttribute(typeof(TargetType))]ek açıklamalı yeni bir sınıf yazarsınız; burada TargetType seri hale getirilen türdür ve 3 serileştirme yordamı uygularsınız. Bu yordamların nasıl yazıldığını gösteren kurallar, uygulanırken IExternalSerializerkullanılanla aynıdır. Orleans[SerializerAttribute(typeof(TargetType))], bu sınıfın için bir seri hale getirici olduğunu belirlemek için TargetType kullanır ve birden çok türü seri hale getirebiliyorsa bu öznitelik aynı sınıfta birden çok kez belirtilebilir. Aşağıda böyle bir sınıf için bir örnek verilmiştir:

public class User
{
    public User BestFriend { get; set; }
    public string NickName { get; set; }
    public int FavoriteNumber { get; set; }
    public DateTimeOffset BirthDate { get; set; }
}

[Orleans.CodeGeneration.SerializerAttribute(typeof(User))]
internal class UserSerializer
{
    [CopierMethod]
    public static object DeepCopier(
        object original, ICopyContext context)
    {
        var input = (User)original;
        var result = new User();

        // Record 'result' as a copy of 'input'. Doing this
        // immediately after construction allows for data
        // structures that have cyclic references or duplicate
        // references. For example, imagine that 'input.BestFriend'
        // is set to 'input'. In that case, failing to record
        // the copy before trying to copy the 'BestFriend' field
        // would result in infinite recursion.
        context.RecordCopy(original, result);

        // Deep-copy each of the fields.
        result.BestFriend =
            (User)context.SerializationManager.DeepCopy(input.BestFriend);

        // strings in .NET are immutable, so they can be shallow-copied.
        result.NickName = input.NickName;
        // ints are primitive value types, so they can be shallow-copied.
        result.FavoriteNumber = input.FavoriteNumber;
        result.BirthDate =
            (DateTimeOffset)context.SerializationManager.DeepCopy(input.BirthDate);

        return result;
    }

    [SerializerMethod]
    public static void Serializer(
        object untypedInput, ISerializationContext context, Type expected)
    {
        var input = (User) untypedInput;

        // Serialize each field.
        SerializationManager.SerializeInner(input.BestFriend, context);
        SerializationManager.SerializeInner(input.NickName, context);
        SerializationManager.SerializeInner(input.FavoriteNumber, context);
        SerializationManager.SerializeInner(input.BirthDate, context);
    }

    [DeserializerMethod]
    public static object Deserializer(
        Type expected, IDeserializationContext context)
    {
        var result = new User();

        // Record 'result' immediately after constructing it.
        // As with the deep copier, this
        // allows for cyclic references and de-duplication.
        context.RecordObject(result);

        // Deserialize each field in the order that they were serialized.
        result.BestFriend =
            SerializationManager.DeserializeInner<User>(context);
        result.NickName =
            SerializationManager.DeserializeInner<string>(context);
        result.FavoriteNumber =
            SerializationManager.DeserializeInner<int>(context);
        result.BirthDate =
            SerializationManager.DeserializeInner<DateTimeOffset>(context);

        return result;
    }
}

Genel türleri seri hale getirme

TargetType parametresi [Serializer(typeof(TargetType))] bir open-generic türü olabilir, örneğin, MyGenericType<T>. Bu durumda, seri hale getirici sınıfı hedef türle aynı genel parametrelere sahip olmalıdır. Orleans seri hale getirilmiş her beton MyGenericType<T> türü için çalışma zamanında seri hale getiricinin somut bir sürümünü oluşturur; örneğin, her biri MyGenericType<int> ve MyGenericType<string>için bir tane.

Seri hale getiriciler ve seri durumdan çıkarıcılar yazma ipuçları

Genellikle seri hale getirici/seri durumdan çıkarıcı çifti yazmanın en basit yolu, bir bayt dizisi oluşturup dizi uzunluğunu akışa yazıp dizinin kendisini yazarak seri durumdan çıkarmak ve ardından işlemi tersine çevirerek seri durumdan çıkarmaktır. Dizi sabit uzunluktaysa, akıştan atlayabilirsiniz. Bu, küçük bir şekilde temsil edebileceğiniz ve yinelenebilen alt nesneleri olmayan bir veri türünüz olduğunda iyi sonuç verir (bu nedenle nesne kimliği konusunda endişelenmeniz gerekmez).

Çalışma zamanının Orleans sözlükler gibi koleksiyonlar için kullandığı yaklaşım olan başka bir yaklaşım, önemli ve karmaşık iç yapısı olan sınıflar için iyi sonuç sağlar: nesnenin anlamsal içeriğine erişmek, bu içeriği seri hale getirmek ve karmaşık iç durum yerine anlamsal içeriği ayarlayarak seri durumdan çıkarmak için örnek yöntemlerini kullanın. Bu yaklaşımda iç nesneler SerializeInner kullanılarak yazılır ve DeserializeInner kullanılarak okunur. Bu durumda, özel bir fotokopi makinesi de yazmak yaygın bir durumdur.

Özel bir seri hale getirici yazarsanız ve sınıf içindeki her alan için SerializeInner çağrısı dizisi gibi görünürse, bu sınıf için özel bir seri hale getiriciye ihtiyacınız yoktur.

Ayrıca bkz.