2012-10-23 15 views
9

Oldukça büyük bir sistemi Remoting to WCF'ye dönüştürdüm ve her şey iyi çalışıyor gibi gözüküyor, ancak şu istisnayı sık sık alıyoruz: "System.InvalidOperationException: Koleksiyon değiştirildi, numaralandırma işlemi yürütülmeyebilir." Takip etmemde hiç şansım olmadı çünkü sadece yüzlerce çağrı geçtiğinde gerçekleşiyor ve bunun sadece bir nesnenin serileştirildiği için değiştirildiğini varsayıyorum.WCF'nin DataContractSerilaizer parçacığı güvenli mi?

Tüm kullanım sınıfları: [DataContract(IsReference=true)].

Remoting kullanıldığında benzer istisnalar yoktu, bu yüzden WCF'de benzer bir sorun olup olmadığını merak edebilirim ya da muhtemelen seri hale getirici olduğunu bana bildirebilir miyim? Bu durumda yazmam gerektiğini varsayalım gereken yerlerde locks gerçekleştirmek için kendi serileştiriciler (kaçınmak istiyorum büyük bir girişimdir).

WCF Error: at System.Collections.Generic.List1.Enumerator.MoveNextRare() at WriteArrayOfLineToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract) at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at WriteLineGroupToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract) at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at WriteLineToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract) at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph) at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph) at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph) at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest) at System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest) at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Channels.BodyWriterMessage.OnWriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer) at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer) at System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(Message message, BufferManager bufferManager, Int32 initialOffset, Int32 maxSizeQuota) at System.ServiceModel.Channels.BinaryMessageEncoderFactory.BinaryMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset) at System.ServiceModel.Channels.FramingDuplexSessionChannel.EncodeMessage(Message message) at System.ServiceModel.Channels.FramingDuplexSessionChannel.OnSend(Message message, TimeSpan timeout) at System.ServiceModel.Channels.OutputChannel.Send(Message message, TimeSpan timeout) at System.ServiceModel.Dispatcher.DuplexChannelBinder.DuplexRequestContext.OnReply(Message message, TimeSpan timeout) at System.ServiceModel.Channels.RequestContextBase.Reply(Message message, TimeSpan timeout) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Reply(MessageRpc& rpc)

+5

sırasında değiştirilip değiştirilmesinin önemi yoktur. Buna değer, hata, "System.InvalidOperationException: Koleksiyon değiştirildi; numaralandırma işlemi yürütülmeyebilir." çok nadiren diş çekme ile ilgili bir şey vardır. Genel olarak, koleksiyonun numaralandırılmasının ortasında modifiye edilmiş olması, çünkü foreach (listedeki var öğe) listesinin yerine getirilmesinin zor olmasıdır. –

+0

Numaralandırmanız nedir? Eğer numaralandırma dizileştirilmeden önce bertaraf edilen bir kaynak kullanıyorsa, aynı hatayı yaşadım. – twreid

+1

@Kirk, eğer durum böyleyse, yığın izi diziselleştirici içinde yapıldığını ve bu nedenle WCF'de bir hata olduğunu öne sürmez miydi? Oldukça eminim ki kodumuz, geçmişte kendimi defalarca yaptığım gibi yapmıyor, bu yüzden de (nihayet) dikkat etmeliyim ki, – user1766568

cevap

4

Gerçekten de, bu hata kolayca DataContractSerializer ile çoğaltılabilir:

aşağıdaki yığın izidir. Eğer benzer yığın izleme IOE alırsınız, kısa yürütme süre sonra

[DataContract] 
public class C 
{ 
    [DataMember] 
    public List<int> Values { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var c = new C 
     { 
      Values = new List<int>() 
     }; 

     var serializer = new DataContractSerializer(typeof(C)); 

     Task 
      .Factory 
      .StartNew(() => 
      { 
       while (true) 
       { 
        Console.WriteLine("Trying to add new item."); 
        c.Values.Add(DateTime.Now.Millisecond); 
       } 
      }, 
      TaskCreationOptions.LongRunning); 

     Task 
      .Factory 
      .StartNew(() => 
      { 
       while (true) 
       { 
        using (var stream = new MemoryStream()) 
        { 
         Console.WriteLine("Trying to serialize."); 
         serializer.WriteObject(stream, c); 
        } 
       } 
      }, 
      TaskCreationOptions.LongRunning); 

     Console.ReadLine(); 
    } 

: O veri sözleşmelerinde kullanılan bazı toplama iplik güvenliği, ilgili, DataContractSerializer güvenliğini iplik ifade etmez

at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) at System.Collections.Generic.List`1.Enumerator.MoveNextRare() at System.Collections.Generic.List`1.Enumerator.MoveNext() at WriteArrayOfintToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract) at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) 

Aynı zamana serileştirilmiş bazı paylaşılan verileri değiştirmeye devam edeceğiniz anlaşılıyor. Hangi işlemin bu hataya neden olduğunu öğrenmek için WCF izlemeyi açabilir (bkz. this sorusu) ve bu işlem tarafından hangi verilerin kullanıldığına dikkatle bakın.

Sonra InstanceContextMode değerleri ve cari hizmet davranışının ConcurrencyMode özelliklerine bağlı olarak, gitmek için hangi yolu seçebilirsiniz:

  • ya bazı kilitleme kullanmak;
  • veya herhangi bir iplik güvenli toplama kullanmak;
  • veya hizmet davranışını değiştirmek için;
  • veya servisin kendisini değiştirmek (ör., Vatansız hale getirmek).
+0

Örnek ve ayrıntılı cevap için teşekkürler. Çoğu hizmet InstanceContextMode.PerCall ve ConcurrencyMode.Mulitple'dir, bu yüzden muhtemelen WCF izlemenin bana gösterdiği şeye bağlı olarak bir thread-safe koleksiyonuyla (ki habersiz) gideceğim. Ancak, açıklamanız olmadan, yine de en iyi çözümleri bile bilmezdim. Teşekkürler! – user1766568

+0

@ user1766568: Umarım, her koleksiyonu onun diş güvenli analoguna değiştirmezsiniz çünkü cehennemin bir yolu budur. – Dennis

1

Eğer Dennis'in hipotezi doğruysa, bunu çözmenin en temiz yolu, koleksiyonun kopyalanması ve kopyanın tel üzerinden gönderilmesidir. Bu noktada, orijinalin

+0

Yanıt için teşekkürler. En kolayı olabilir, ancak nesne grafikleri oldukça derin olabilir, bu yüzden bu durumda en hızlısı olacağından emin değilim. Koleksiyona sahip olabilecek veya koleksiyonları vb. Olan nesnelerin bir koleksiyonuna sahip olabilecek bir nesneyi iade ediyorum. bu yüzden thread-safe koleksiyonları ile gitmem gerekebilir. – user1766568

+0

Bu bir hipotez değil. :) Bundan emin olmak için kod örneğini derleyebilirsiniz. – Dennis

+0

Kodunuzun sorunu göstermediğini söylemiyorum. Söylediğim şey, kodunuzun aynı belirtiye sahip olması, OP ile aynı nedenin olduğu anlamına gelmez. Ancak kodunuz aynı nedenden kaynaklanıyorsa, kopyalama sorunu çözmek için temiz bir yoldur –

İlgili konular