سؤال

لدينا بعض المشاكل في تسلسل قائمة فارغة. هنا بعض التعليمات البرمجية في .NET باستخدام CF 2.0

//Generating the protobuf-msg
ProtoBufMessage msg = new ProtoBufMessage();
msg.list = new List<AnotherProtobufMessage>();
// Serializing and sending throw HTTP-POST
MemoryStream stream = new MemoryStream();
Serializer.Serialize(stream, msg);
byte[] bytes = stream.ToArray();
HttpWebRequest request = createRequest();
request.ContentLength = bytes.Length ;

using (Stream httpStream = request.GetRequestStream())
{              
      httpStream.Write(bytes, 0, bytes.Length);
}

حصلنا على استثناء ، عندما نحاول الكتابة على الدفق (Bytes.Length خارج النطاق). ولكن لا ينبغي أن يكون النوع الذي يحتوي على قائمة فارغة 0 بايت ، يمين (معلومات النوع؟)؟

نحتاج إلى هذا النوع من الإرسال ، لأنه في الاستجابة هي الرسائل من الخادم لعميلنا.

هل كانت مفيدة؟

المحلول

تنسيق السلك (المحدد بواسطة Google - وليس داخل جهاز التحكم الخاص بي!) يرسل فقط البيانات لـ العناصر. لا يميز بين فارغة قائمة و لا شيء قائمة. لذلك إذا لم يكن هناك بيانات لإرسالها - نعم ، فإن الطول هو 0 (إنه تنسيق مقتصد للغاية ؛ -P).

لا تتضمن المخازن المؤقتة للبروتوكول أي بيانات تعريف على السلك.

GOTCHA شائع آخر هنا هو أنك قد تفترض أن خاصية القائمة الخاصة بك يتم تثبيتها تلقائيًا على أنها فارغة ، ولكنها لن تكون (ما لم يفعل الكود الخاص بك ، ربما في مُهيئ الحقل أو مُنشئ).

هذا اختراق عملي:

[ProtoContract]
class SomeType {

    [ProtoMember(1)]
    public List<SomeOtherType> Items {get;set;}

    [DefaultValue(false), ProtoMember(2)]
    private bool IsEmptyList {
        get { return Items != null && Items.Count == 0; }
        set { if(value) {Items = new List<SomeOtherType>();}}
    }
}

اختراق ربما ، ولكن يجب أن يعمل. يمكنك أيضا أن تفقد Items "تعيين" إذا كنت تريد وأسقط فقط bool:

    [ProtoMember(1)]
    public List<SomeOtherType> Items {get {return items;}}
    private readonly List<SomeOtherType> items = new List<SomeOtherType>();

    [DefaultValue(false), ProtoMember(2)]
    private bool IsEmptyList {
        get { return items.Count == 0; }
        set { }
    }

نصائح أخرى

كما قال MARC ، فإن تنسيق السلك يرسل فقط بيانات للعناصر ، لذلك لمعرفة ما إذا كانت القائمة فارغة أو فارغة ، يجب عليك إضافة هذه المعلومات إلى الدفق.
إضافة خاصية إضافية للإشارة إلى ما إذا كانت المجموعة الأصلية فارغة أم لا أمر سهل ، ولكن إذا كنت لا ترغب في تعديل تعريف النوع الأصلي ، فلديك خياران آخران:

تسلسل باستخدام بديل

سيكون لنوع البديل الخاصية الإضافية (الحفاظ على نوعك الأصلي دون مساس) وسيقوم باستعادة الحالة الأصلية للقائمة: فارغة ، مع عناصر أو فارغة.

    [TestMethod]
    public void SerializeEmptyCollectionUsingSurrogate_RemainEmpty()
    {
        var instance = new SomeType { Items = new List<int>() };

        // set the surrogate
        RuntimeTypeModel.Default.Add(typeof(SomeType), true).SetSurrogate(typeof(SomeTypeSurrogate));

        // serialize-deserialize using cloning
        var clone = Serializer.DeepClone(instance);

        // clone is not null and empty
        Assert.IsNotNull(clone.Items);
        Assert.AreEqual(0, clone.Items.Count);
    }

    [ProtoContract]
    public class SomeType
    {
        [ProtoMember(1)]
        public List<int> Items { get; set; }
    }

    [ProtoContract]
    public class SomeTypeSurrogate
    {
        [ProtoMember(1)]
        public List<int> Items { get; set; }

        [ProtoMember(2)]
        public bool ItemsIsEmpty { get; set; }

        public static implicit operator SomeTypeSurrogate(SomeType value)
        {
            return value != null
                ? new SomeTypeSurrogate { Items = value.Items, ItemsIsEmpty = value.Items != null && value.Items.Count == 0 }
                : null;
        }

        public static implicit operator SomeType(SomeTypeSurrogate value)
        {
            return value != null
                ? new SomeType { Items = value.ItemsIsEmpty ? new List<int>() : value.Items }
                : null;
        }
    }


اجعل الأنواع الخاصة بك قابلة للتمديد

تشير Protobuf-Net إلى واجهة iextensible التي تتيح لك تمديد الأنواع بحيث يمكن إضافة الحقول إلى رسالة دون أي شيء (اقرأ المزيد هنا). من أجل استخدام امتداد Protobuf-Net ، يمكنك أن ترث Extensible الفصل أو تنفيذ IExtensible واجهة لتجنب قيود الميراث.
الآن بعد أن أصبح نوعك "قابل للتمديد" الذي تحدده [OnSerializing] و [OnDeserialized] طرق لإضافة المؤشرات الجديدة التي سيتم تسلسلها إلى التيار وتخلص منها عند إعادة بناء الكائن بحالته الأصلية.
الايجابيات هي أنك لست بحاجة إلى تحديد خصائص جديدة أو أنواع جديدة كبديل ، والسلبيات هي ذلك IExtensible غير مدعوم إذا كان النوع الخاص بك يحتوي على أنواع فرعية محددة في نموذج النوع الخاص بك.

    [TestMethod]
    public void SerializeEmptyCollectionInExtensibleType_RemainEmpty()
    {
        var instance = new Store { Products = new List<string>() };

        // serialize-deserialize using cloning
        var clone = Serializer.DeepClone(instance);

        // clone is not null and empty
        Assert.IsNotNull(clone.Products);
        Assert.AreEqual(0, clone.Products.Count);
    }

    [ProtoContract]
    public class Store : Extensible
    {
        [ProtoMember(1)]
        public List<string> Products { get; set; }

        [OnSerializing]
        public void OnDeserializing()
        {
            var productsListIsEmpty = this.Products != null && this.Products.Count == 0;
            Extensible.AppendValue(this, 101, productsListIsEmpty);
        }

        [OnDeserialized]
        public void OnDeserialized()
        {
            var productsListIsEmpty = Extensible.GetValue<bool>(this, 101);
            if (productsListIsEmpty)
                this.Products = new List<string>();
        }
    }
public List<NotificationAddress> BccAddresses { get; set; }

يمكنك الاستبدال بـ:

private List<NotificationAddress> _BccAddresses;
public List<NotificationAddress> BccAddresses {
   get { return _BccAddresses; }
   set { _BccAddresses = (value != null && value.length) ? value : null; }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top