2010-03-09 3 views
0

edit: Comme mentionné dans mon commentaire, j'ai découvert que la raison de ce problème était que l'objet Module a une référence à l'objet OrderInfo. DataContractSerializer ne prend pas en charge la préservation des références d'objet par défaut. J'ai maintenant réussi à faire fonctionner tout cela correctement. Si quelqu'un est intéressé, contactez-moi et je l'ajouterai dans une réponse ici.Problème de préservation des références d'objet bidirectionnel dans WCF. Message d'erreur: "La connexion sous-jacente a été fermée: La connexion a été fermée de façon inattendue."

  • .net sert au client .net avec la bibliothèque POCO (objets de données) partagée aux deux extrémités.
  • Objet OrderInfo contient une liste. Si la liste contient des objets de module, je suis redouté "La connexion sous-jacente était fermée: la connexion a été fermée de manière inattendue."
  • Je peux envoyer une liste "autonome" à partir d'une autre méthode de service et cela fonctionne très bien afin que les objets Module sérialisent/désérialisent correctement. .
  • Je ne me DataContract dans les classes POCO, WCF gère cela automatiquement (qui pourrait aussi être le problème que je l'ai essayé d'ajouter:

    [Serializable] 
    [XmlInclude(typeof(List<Module>))] 
    

mais cela n'a pas aidé. Je ne vois pas quel est le problème que je fais la même chose EXACT dans l'objet du module retournant une collection d'objets Pricemodel

public class OrderInfo 
    { 
    int _ProductID; 
    IList<Module> _Modules = new List<Module>(); 
    //IList<MiscProduct> _MiscProduct = new List<MiscProduct>(); 

    public IList<Module> Modules 
    { 
     get 
     { 
      return new List<Module>(_Modules).AsReadOnly(); 
     } 
     set 
     { 
      _Modules = value; 
     } 
    } 
    } 

    public class Module 
    { 
    string _Name; 
    int _Sort_Number; 
    string _Description; 
    OrderInfo _OrderInfoMaster; 
    IList<Pricemodel> _Pricemodels = new List<Pricemodel>(); 

    public IList<Pricemodel> Pricemodels 
    { 
     get 
     { 
      return new List<Pricemodel>(_Pricemodels).AsReadOnly(); 
     } 
     set 
     { 
      _Pricemodels = value; 
     } 
    } 
    } 

appel code client est:.

using (ProductOrderItems_WCFService.ProductOrderServiceClient client = new ProductOrderItems_WCFService.ProductOrderServiceClient()) 
    { 
     string s = client.HelloWorld(); 
     Module m = client.GetModule(); 
     List<Module> mods = client.GetModuleList(7); 
     grdModules.DataSource = mods; 
     grdModules.DataBind(); 

     OrderInfo oi = client.GetOrderInfo(7); 
    } 

Il échoue sur la dernière ligne lorsque je demande l'objet OrderInfo du service. Tous les appels ci-dessus fonctionnent très bien.

+0

J'ai réussi à comprendre que cela se produit parce que l'objet Module a une référence "retour" à l'objet OrderInfo parent. J'ai essayé d'activer "PreserveObjectReferences" dans un DataContractSerializerOperationBehavior personnalisé comme indiqué à la fin de cet article: http: //www.zamd.net/2008/05/20/DataContractSerializerAndIsReferenceProperty.aspx Mais je ne pouvais pas le faire fonctionner et j'ai abandonné pour le moment. Je ne voulais pas utiliser d'attributs dans mes classes POCO mais je pourrais donner un coup avec le [DataContract (IsReference = true)] sur mes classes. Je crois que l'équipe de la WCF devrait faciliter cela! – Trygve

+0

oui, c'est à peu près mon problème en ce moment aussi. Ne pas marquer les classes POCO avec quelque chose, mais la façon dont Entity Framework charge les propriétés de navigation, je ne peux tout simplement pas échapper à essayer de sérialiser une référence circulaire. J'ai posté une question ici: http://stackoverflow.com/questions/2915181/ef4-poco-wcf-serialization-problems-no-lazy-loading-proxy-no-proxy-circular-re – kdawg

Répondre

0

D'abord la coutume DataContactSerializerOperationBehavior

using System; 
using System.ServiceModel.Description; 
using System.Runtime.Serialization; 
using System.Collections.Generic; 

/// <summary> 
/// Summary description for ReferencePreservingDataContractSerializerOperationBehavior 
/// </summary> 
public class ReferencePreservingDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior 
{ 
    public ReferencePreservingDataContractSerializerOperationBehavior(OperationDescription operation) : base(operation) 
    { 
    } 

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes) 
    { 
     return new DataContractSerializer(type, name, ns, knownTypes, this.MaxItemsInObjectGraph, this.IgnoreExtensionDataObject, true, this.DataContractSurrogate); 
    } 

    public override XmlObjectSerializer CreateSerializer(Type type, System.Xml.XmlDictionaryString name, System.Xml.XmlDictionaryString ns, IList<Type> knownTypes) 
    { 
     return new DataContractSerializer(type, name, ns, knownTypes, this.MaxItemsInObjectGraph, this.IgnoreExtensionDataObject, true, this.DataContractSurrogate); 
    } 
} 

est le suivant SelfDescribingServiceHost pour nous permettre d'utiliser la ReferencePreservingDataContractSerializerOperationBehavior

using System; 
using System.ServiceModel; 
using System.ServiceModel.Description; 

namespace NewWcfService 
{ 
    //This class is a custom derivative of ServiceHost 
    //that can automatically enabled metadata generation 
    //for any service it hosts. 
    class SelfDescribingServiceHost : ServiceHost 
    { 
     public SelfDescribingServiceHost(Type serviceType, params Uri[] baseAddresses) 
      : base(serviceType, baseAddresses) 
     { 
     } 

     //Overriding ApplyConfiguration() allows us to 
     //alter the ServiceDescription prior to opening 
     //the service host. 
     protected override void ApplyConfiguration() 
     { 
      //First, we call base.ApplyConfiguration() 
      //to read any configuration that was provided for 
      //the service we're hosting. After this call, 
      //this.ServiceDescription describes the service 
      //as it was configured. 
      base.ApplyConfiguration(); 

      foreach (ServiceEndpoint endpoint in this.Description.Endpoints) 
       SetDataContractSerializerBehavior(endpoint.Contract); 

      //Now that we've populated the ServiceDescription, we can reach into it 
      //and do interesting things (in this case, we'll add an instance of 
      //ServiceMetadataBehavior if it's not already there. 
      ServiceMetadataBehavior mexBehavior = this.Description.Behaviors.Find<ServiceMetadataBehavior>(); 
      if (mexBehavior == null) 
      { 
       mexBehavior = new ServiceMetadataBehavior(); 
       this.Description.Behaviors.Add(mexBehavior); 
      } 
      else 
      { 
       //Metadata behavior has already been configured, 
       //so we don't have any work to do. 
       return; 
      } 

      //Add a metadata endpoint at each base address 
      //using the "/mex" addressing convention 
      foreach (Uri baseAddress in this.BaseAddresses) 
      { 
       if (baseAddress.Scheme == Uri.UriSchemeHttp) 
       { 
        mexBehavior.HttpGetEnabled = true; 
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, 
              MetadataExchangeBindings.CreateMexHttpBinding(), 
              "mex"); 
       } 
       else if (baseAddress.Scheme == Uri.UriSchemeHttps) 
       { 
        mexBehavior.HttpsGetEnabled = true; 
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, 
              MetadataExchangeBindings.CreateMexHttpsBinding(), 
              "mex"); 
       } 
       else if (baseAddress.Scheme == Uri.UriSchemeNetPipe) 
       { 
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, 
              MetadataExchangeBindings.CreateMexNamedPipeBinding(), 
              "mex"); 
       } 
       else if (baseAddress.Scheme == Uri.UriSchemeNetTcp) 
       { 
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, 
              MetadataExchangeBindings.CreateMexTcpBinding(), 
              "mex"); 
       } 
      } 

     } 

     private static void SetDataContractSerializerBehavior(ContractDescription contractDescription) 
     { 
      foreach (OperationDescription operation in contractDescription.Operations) 
      { 
       DataContractSerializerOperationBehavior dcsob = operation.Behaviors.Find<DataContractSerializerOperationBehavior>(); 
       if (dcsob != null) 
       { 
        operation.Behaviors.Remove(dcsob); 
       } 
       operation.Behaviors.Add(new ReferencePreservingDataContractSerializerOperationBehavior(operation)); 
      } 
     } 
    } 
} 

Ensuite, il y a le ServiceHostFactory:

using System; 
using System.ServiceModel; 
using System.ServiceModel.Activation; 

namespace NewWcfService 
{ 
     public class SelfDescribingServiceHostFactory : ServiceHostFactory 
    { 
     protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 
     { 
      //All the custom factory does is return a new instance 
      //of our custom host class. The bulk of the custom logic should 
      //live in the custom host (as opposed to the factory) for maximum 
      //reuse value. 
      return new SelfDescribingServiceHost(serviceType, baseAddresses); 
     } 
    } 
} 

Et bien sûr Service.svc pour utiliser la nouvelle HostFactory: <%@ ServiceHost Language="C#" Debug="true" Service="NewWcfService.Service" Factory="ProTeriaWCF.SelfDescribingServiceHostFactory" CodeBehind="Service.svc.cs" %>

0

Il peut y avoir deux raisons à cette erreur;

  1. Si votre objet de retour a des objets récursifs, je veux dire, votre objet de retour et objets à l'intérieur comprend l'autre, que cela crée problème de sérialisation. Vous devez couper des objets récursifs à un niveau que vous pouvez décider.

  2. Les énumérations avec la valeur 0 peuvent causer ce problème.

+0

1. D'accord, est un problème si la récursivité est trop profonde. 2. Je n'étais pas au courant de cela – Trygve

+0

Par exemple, si vous essayez de retourner en dessous de l'énumération de la valeur 0, il renvoie la même erreur. Parce que la valeur 0 n'est pas identifiée. public enum {StateIdentity Deleted = 1, Rejeté = 2, taxée = 3, Passif = 4, } – NetSide

Questions connexes