2010-03-09 2 views
0

Редактировать: Как уже упоминалось в моем комментарии, я выяснил, почему эта проблема связана с тем, что объект модуля ссылается на объект OrderInfo. DataContractSerializer не поддерживает сохранение ссылок на объекты по умолчанию. Теперь я смог заставить все это работать правильно. Если кто-то заинтересован, свяжитесь со мной, и я добавлю его в ответ здесь.Проблема сохранения двухсторонних ссылок объектов в WCF. Сообщение об ошибке: «Основное соединение было закрыто: соединение было неожиданно закрыто».

  • .net сервис .net-клиенту с общей библиотекой POCO (объекты данных) на обоих концах.
  • Объект OrderInfo содержит список. Если в списке содержатся объекты модуля, я получаю страшное «Основное соединение было закрыто: соединение было неожиданно закрыто».
  • Я могу отправить «автономный» список из другого метода службы, и он отлично работает, поэтому модуль сам по себе сериализует/десериализует штраф. .
  • Я не использую DataContract в классах POCO, WCF обрабатывает это автоматически (который также может быть проблема, которую я пытался добавить:

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

, но это не помогло. Я не вижу, в чем проблема, так как я делаю одно и то же в объекте модуля возвращает коллекцию объектов 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; 
     } 
    } 
    } 

телефонный код клиента:.

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); 
    } 

Он не работает в последней строке, когда я запрашиваю объект OrderInfo из службы. Все вышеперечисленные вызовы отлично работают.

+0

Мне удалось выяснить, что это происходит потому, что объект модуля имеет ссылку «назад» на родительский объект OrderInfo. Я попытался включить «PreserveObjectReferences» в пользовательском DataContractSerializerOperationBehavior, как описано в конце этой статьи: http: //www.zamd.net/2008/05/20/DataContractSerializerAndIsReferenceProperty.aspx Но я не мог заставить его работать и на данный момент отказался. Я не хотел использовать какие-либо атрибуты в своих классах POCO, но я мог бы дать этот снимок с помощью [DataContract (IsReference = true)] на моих классах. Я считаю, что команда WCF должна сделать это проще! – Trygve

+0

Да, это в значительной степени моя проблема прямо сейчас. Не выделяйте классы POCO ничем, но способ, которым Entity Framework загружает свойства навигации, я просто не могу уйти от попытки сериализации циклической ссылки. Я поставил вопрос здесь: http://stackoverflow.com/questions/2915181/ef4-poco-wcf-serialization-problems-no-lazy-loading-proxy-no-proxy-circular-re – kdawg

ответ

0

Первый обычай 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); 
    } 
} 

Далее идет SelfDescribingServiceHost, чтобы позволить нам использовать 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)); 
      } 
     } 
    } 
} 

Тогда есть 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); 
     } 
    } 
} 

И, конечно, the Service.svc использовать новый HostFactory: <%@ ServiceHost Language="C#" Debug="true" Service="NewWcfService.Service" Factory="ProTeriaWCF.SelfDescribingServiceHostFactory" CodeBehind="Service.svc.cs" %>

0

Возможны две причины этой ошибки;

  1. Если ваше возвращение объект имеет рекурсивные объекты, я имею в виду, что ваш объект возврата и внутри объекты включают в себя друг с другом, чем это создает проблему сериализации. Вам нужно сократить рекурсивные объекты на уровне, который вы можете решить.

  2. Перечисления со значением 0 могут вызвать эту проблему.

+0

1. Согласен, является проблемой, если рекурсия слишком глубокая. 2. Я не знал об этом – Trygve

+0

Например, если вы попытаетесь вернуться ниже перечисления значения 0, он выдает такую ​​же ошибку. Поскольку значение 0 не определено. общественное перечисление StateIdentity { Исключен = 1, Отклонено = 2, Paid = 3, Пассивный = 4, } – NetSide

Смежные вопросы

 Смежные вопросы