Проблема сохранения двухсторонних ссылок на объекты в WCF. Сообщение об ошибке: «Базовое соединение было закрыто: соединение было неожиданно закрыто». - PullRequest
0 голосов
/ 09 марта 2010

edit: Как уже упоминалось в моем комментарии, я обнаружил, что причиной этой проблемы было то, что объект Module имеет ссылку на объект OrderInfo. DataContractSerializer не поддерживает сохранение ссылок на объекты по умолчанию. Теперь я смог заставить все это работать правильно. Если кому-то интересно, свяжитесь со мной, и я добавлю его в ответ здесь.

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

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

но это не помогло. Я не понимаю, в чем проблема, поскольку я делаю ТОЧНО ЖЕ в объекте 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 из сервиса. Все вышеперечисленные звонки прекрасно работают.

Ответы [ 2 ]

0 голосов
/ 01 апреля 2011

Причин этой ошибки может быть две:

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

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

0 голосов
/ 01 апреля 2011

Сначала пользовательский 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);
        }
    }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...