6

मैं एक नए ऐप संस्करण में एक निश्चित प्रोटोबफ-सीरियलाइज्ड क्लास के लिए एक नया एनम मान जोड़ने की कोशिश कर रहा था, और परीक्षण करते समय, ध्यान दिया गया कि पिछले संस्करण को एक नया अपवाद फेंक दिया जाएगा, :प्रोटोबफ-नेट एनम पिछड़ा संगतता

 
An unhandled exception of type 'ProtoBuf.ProtoException' occurred in protobuf-net.dll 
Additional information: No {enum-type-name} enum is mapped to the wire-value 3 

यह काफी स्पष्ट है कि यह मुझे बता रही है 3 की int मूल्य के लिए कोई enum मूल्य नहीं होता है कि है, लेकिन मैं हमेशा विचार था कि Protocol Buffers defaulted to the zero-valued ("default") enum value (यदि ऐसा मौजूद है), के मामले में है कि एक वास्तविक enum मूल्य मैप नहीं किया जा सका।

स्पष्ट करने के लिए, इस reproduced किया जा सकता है कि निम्न उदाहरण (मैं जानबूझकर एक अलग वर्ग में अक्रमांकन कदम कर रहा हूँ नए प्रारूप लोड करने का प्रयास पुराने एप्लिकेशन को नकल करने के लिए) का उपयोग कर:

// --- version 1 --- 

public enum EnumV1 
{ 
    Default = 0, 
    One = 1, 
    Two = 2 
} 

[ProtoContract] 
public class ClassV1 
{ 
    [ProtoMember(1)] 
    public EnumV1 Value { get; set; } 
} 



// --- version 2 --- 

public enum EnumV2 
{ 
    Default = 0, 
    One = 1, 
    Two = 2, 
    Three = 3 // <- newly added 
} 

[ProtoContract] 
public class ClassV2 
{ 
    [ProtoMember(1)] 
    public EnumV2 Value { get; set; } 
} 

और निम्नलिखित कोड असफल हो जायेगी:

// serialize v2 using the new app 
var v2 = new ClassV2() { Value = EnumV2.Three }; 
var v2data = Serialize(v2); 

// try to deserialize this inside the old app to v1 
var v1roundtrip = Deserialize<ClassV1>(v2data); 

v1 के बाद से खुले में बाहर है, वहाँ कुछ मेटाडाटा जब वी 2 में serializing इस समस्या से बचने के लिए मैं उपयोग कर सकते हैं? मैं निश्चित रूप से, एक अलग संपत्ति का उपयोग करने के लिए v2 को फिर से लिखकर इस समस्या से खुद को प्राप्त कर सकता हूं और enum मानों को अनमोडिफाइड छोड़ सकता हूं, लेकिन यदि संभव हो तो मैं पीछे की तरफ संगत बनाना चाहता हूं।

+0

'v1roundtrip.Value' का क्या होगा जब' EnumV2.Three' भेजा गया था: यहाँ EnumSerializer.cs अंदर समस्याग्रस्त कोड (ISSUE #422 टिप्पणी मेरी है) है? – Caramiriel

+1

@Caramiriel: मेरी समझ के अनुसार ([इस धागे] में वर्णित है (http://stackoverflow.com/q/10392952/69809)), इसे अपवाद फेंकने के बजाय 'EnumV1.Default' पर सेट किया जाना चाहिए था। अगर मैं यह सुनिश्चित करना चाहता हूं कि प्रारूप पीछे की ओर संगत है तो मैं यही उम्मीद करूंगा। उदाहरण के लिए, [इस उपयोगकर्ता] (http://stackoverflow.com/a/13924171/69809) में एक ही समस्या थी, और डिफ़ॉल्ट (शून्य) enum मान जोड़कर इसे ठीक किया गया, जिसमें कोई अतिरिक्त प्रोटोबफ गुण आवश्यक नहीं था। – Groo

+2

पिंग @marcgravell – jgauffin

उत्तर

1

के बाद से v1 खुले में बाहर है, वहाँ कुछ मेटाडाटा जब वी 2 में serializing इस समस्या से बचने के लिए मैं उपयोग कर सकते हैं? मैं निश्चित रूप से, एक अलग संपत्ति का उपयोग करने के लिए v2 को फिर से लिखकर इस समस्या से खुद को प्राप्त कर सकता हूं और enum मानों को अनमोडिफाइड छोड़ सकता हूं, लेकिन यदि संभव हो तो मैं पीछे की तरफ संगत बनाना चाहता हूं।

आप क्या अनुभव कर रहे हैं एक Protobuf शुद्ध बग यहाँ protobuf-net - issue #422: Invalid behaviour while deserializing unknown enum value वर्णित है।

ऐसा लगता है कि यह अभी तक protobuf-net faulty enum exception (issue 422) need a good workaround (और निश्चित रूप से आपकी पोस्ट) के अनुसार तय नहीं है।

दुर्भाग्यवश आपको या तो protobuf-net स्रोत कोड को ठीक करने या वर्णित कार्यवाही का उपयोग करने की आवश्यकता है।

अद्यतन: मैंने GitHub भंडार में कोड की जांच की है और यह पुष्टि कर रही है कि समस्या अभी भी तय नहीं है।

public object Read(object value, ProtoReader source) 
{ 
    Helpers.DebugAssert(value == null); // since replaces 
    int wireValue = source.ReadInt32(); 
    if(map == null) { 
     return WireToEnum(wireValue); 
    } 
    for(int i = 0 ; i < map.Length ; i++) { 
     if(map[i].WireValue == wireValue) { 
      return map[i].TypedValue; 
     } 
    } 
    // ISSUE #422 
    source.ThrowEnumException(ExpectedType, wireValue); 
    return null; // to make compiler happy 
} 
0

आपकी कक्षावी 1 में आगे की compatiblity की कमी है।

मैंने प्रोटो अनुबंध को इस तरह से कार्यान्वित किया होगा कि यह enum मूल्य के स्ट्रिंग प्रस्तुति को क्रमबद्ध/deserializes। इस तरह आप अपने द्वारा डिफ़ॉल्ट मूल्य पर फ़ॉलबैक को संभाल सकते हैं। मूल्य संपत्ति को क्रमबद्ध/deserialized नहीं किया जाएगा।

public enum EnumV1 
{ 
    Default = 0, 
    One = 1, 
    Two = 2 
} 

public enum EnumV2 
{ 
    Default = 0, 
    One = 1, 
    Two = 2, 
    Three = 3 // <- newly added 
} 

[ProtoContract] 
public class ClassV1 
{ 
    [ProtoMember(1)] 
    public string ValueAsString 
    { 
     get { return Value.ToString(); } 
     set 
     { 
      try 
      { 
       Value = (EnumV1) Enum.Parse(typeof (EnumV1), value); 
      } 
      catch (Exception) 
      { 
       Value = EnumV1.Default; 
      } 
     } 
    } 

    public EnumV1 Value { get; set; } 
} 

[ProtoContract] 
public class ClassV2 
{ 
    [ProtoMember(1)] 
    public string ValueAsString 
    { 
     get { return Value.ToString(); } 
     set 
     { 
      try 
      { 
       Value = (EnumV2)Enum.Parse(typeof(EnumV2), value); 
      } 
      catch (Exception) 
      { 
       Value = EnumV2.Default; 
      } 
     } 
    } 

    public EnumV2 Value { get; set; } 
} 

फिर भी यह उत्पादन में गैर-आगे-संगत वर्ग रखने की समस्या का समाधान नहीं करता है।

+0

पीछे/आगे संगतता सुनिश्चित करने के कई तरीके हैं। एक आसान तरीका (एक एनम के लिए) जो काम करेगा, 'int' मान को डी/क्रमबद्ध करना होगा। लेकिन यह वही नहीं है जो मैंने किया था, क्योंकि मैंने इसे संभालने में सक्षम होने के लिए प्रोटोबफ पर भरोसा किया था। मैंने v1 अनुबंध को डिज़ाइन किया है कि * प्रोटोकॉल बफर शून्य-मूल्यवान एनम मान पर चूक गए हैं, यदि वास्तविक एनम मान को मैप नहीं किया जा सका, और मैं देखना चाहता हूं 1) यह क्यों काम नहीं करता है और 2) कम से कम इस मामले को अवरुद्ध करने और संभालने के दौरान कैसे संभालें। आपका जवाब न तो बताता है * क्यों * 'कक्षावी 1 'में एफडब्ल्यू संगतता की कमी है, न ही अब क्या करना है। – Groo

+0

आगे संगतता की कमी क्लासवी 1 से नई एनम मूल्यों का समर्थन नहीं करती है, जिसे संकलन-समय-स्थिरांक वाले मूल्य प्रकार के रूप में माना जा सकता है। यह डिफ़ॉल्ट शाखा के बिना स्विच/केस स्टेटमेंट को कार्यान्वित करने जैसा है और इसे असमर्थित मामलों को संभालने दें। –

+0

मुझे नहीं लगता कि डिफ़ॉल्ट मान निर्धारित करना प्रोटोबफ की ज़िम्मेदारी है। मैं क्लास क्लासवी 1 को इंस्टेंसियस के दौरान संपत्ति शुरू करके इसे स्वयं शुरू कर दूंगा, इसे प्रारंभिक छोड़ने के बजाय (बुलियन फ़ील्ड भी झूठी झूठी बात के साथ शुरू किया जाता है)। प्रोटो-बफ गैर-निर्बाध फेंक देगा जो ठीक है क्योंकि इससे पता चलता है कि एक संगत समस्या है। –

0

आप अपने प्रोटो सदस्य संपत्ति में डिफ़ॉल्ट वैल्यू विशेषता जोड़ सकते हैं।

[ProtoContract] 
public class ClassV1 
{ 
    [ProtoMember(1), DefaultValue(EnumV1.Default)] 
    public EnumV1 Value { get; set; } 
} 

यह स्पष्ट करने के लिए कि डिफ़ॉल्ट मामले के लिए संपत्ति को कैसे प्रारंभ किया जाना चाहिए।

+0

क्या आपने इसे आजमाया? मुझे नहीं लगता कि इसका अपवाद के साथ कुछ भी करना है। – Groo

3

आपके enums में [ProtoContract(EnumPassthru=true)] जोड़ना प्रोटोबफ-नेट को अज्ञात मानों को deserialize करने की अनुमति देगा।

दुर्भाग्यवश, आपके v1 को पूर्ववत करने का कोई तरीका नहीं है। आपको एक अलग संपत्ति का उपयोग करना होगा।

+0

लेकिन क्या यह डिफ़ॉल्ट मान के बजाय deserialized मान '3' (भले ही उसके पास मिलान करने वाला enum नहीं था) सेट नहीं करेगा? मुझे अभी भी वास्तविक मूल्य का समाधान करने के लिए डिफ़ॉल्ट मान का उपयोग करने की उम्मीद है? – Groo

+0

हां। यदि आपको पुराने संस्करण के मान को डिफ़ॉल्ट होने के लिए और नए संस्करण पर मूल्य कुछ नए enum मान (इस मामले में, 3) होने की आवश्यकता है, तो आपको हर बार जब आप जोड़ते हैं तो एक नई संपत्ति पर स्विच करना होगा आपके enum के लिए एक नया मूल्य। – yaakov

संबंधित मुद्दे