在WCF中保留双向对象引用的问题。错误消息:“基础连接已关闭:连接意外关闭。”

时间:2010-03-09 10:07:51

标签: c# .net wcf

编辑正如我在评论中提到的,我发现这个问题的原因是Module对象有一个返回OrderInfo对象的引用。默认情况下,DataContractSerializer不支持保留对象引用。我现在已经能够正常工作了。如果有人有兴趣请联系我,我会在这里添加答案。

  • .net服务到两端共享POCO(数据对象)库的.net客户端。
  • Object OrderInfo包含一个List。如果列表包含任何Module对象,我会得到可怕的“底层连接已关闭:连接意外关闭。”
  • 我可以从另一个服务方法发送一个“独立”列表,它工作正常,因此模块对象可以自行序列化/反序列化。
  • 我不在POCO类中使用datacontract,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 :(得分: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);
        }
    }
}

当然Service.svc使用新的HostFactory:     <%@ ServiceHost Language="C#" Debug="true" Service="NewWcfService.Service" Factory="ProTeriaWCF.SelfDescribingServiceHostFactory" CodeBehind="Service.svc.cs" %>

答案 1 :(得分:0)

此错误可能有两个原因;

  1. 如果你的返回对象有递归对象,我的意思是你的返回对象和内部对象相互包含,而不是创建序列化问题。您需要在可以决定的级别上剪切递归对象。

  2. 值为0的枚举可能会导致此问题。