【问题标题】:Use base class as parameter for WCF Service使用基类作为 WCF 服务的参数
【发布时间】:2017-01-04 08:52:41
【问题描述】:

我有一个多项目解决方案。 一个项目正在提供一个包含多个类的 DLL。

其中一个类是WorkerTemplate。另外两个类继承自它,即ExecSQLWorkerCopyWorker

class ExecSQLWorker : WorkerTemplate {};
class CopyWorker: WorkerTemplate {};

在我的 WCF 服务中,我的界面是这样的:

 public interface IPQWService
 {
     [OperationContract]
     void EnqueueWorker(WorkerTemplate[] worker);
 }

现在在我的客户端应用程序中,WorkerTemplate[]ExecSQLWorkerCopyWorker 对象组成。当我现在尝试调用 EnqueueWorker(worker) 方法时,我收到一条错误消息,告诉我序列化工作类时出现问题。

所以我猜这是因为该服务在序列化基类时确实存在问题,而是获得了继承类。但是如何轻松解决这个问题呢?

编辑:例外:

Unhandled Exception: System.ServiceModel.CommunicationException: There was an error while trying to serialize parameter http://tempuri.org/:worker. The InnerException message was 'Type 'DV_BII30.ExecSQLWorker' with data contract name 'ExecSQLWorker:http://schemas.datacontract.org/2004/07/DV_BII30' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to the serializer.'.  Please see InnerException for more details. ---> System.Runtime.Serialization.SerializationException: Type 'DV_BII30.ExecSQLWorker' with data contract name 'ExecSQLWorker:http://schemas.datacontract.org/2004/07/DV_BII30' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to the serializer.
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
   at WriteArrayOfWorkerTemplateToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract )
   at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   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)
   --- End of inner exception stack trace ---

Server stack trace:
   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.SerializeParameters(XmlDictionaryWriter writer, PartInfo[] parts, Object[] parameters)
   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.BodyWriterMessage.OnWriteBodyContents(XmlDictionaryWriter writer)
   at System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer)
   at System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(Message message, BufferManager bufferManager, Int32 initialOffset, Int32 maxSizeQuota)
   at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset)
   at System.ServiceModel.Channels.HttpOutput.SerializeBufferedMessage(Message message, Boolean shouldRecycleBuffer)
   at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at DV_BII30.ServiceReference1.IPQWService.EnqueueWorker(WorkerTemplate[] worker)
   at DV_BII30.Program.Main(String[] args) in C:\TFS\robert.hartmann\PCO\Features\CDW5.0 Prototyping\Sources\DTSx\DataVault_Staging_CDW\DV_BII30\Program.cs:line 119

【问题讨论】:

  • 请发布例外情况。
  • 对不起。我加了。

标签: c# wcf polymorphism


【解决方案1】:

错误信息告诉你该怎么做:

不应出现 InnerException 消息为“类型 'DV_BII30.ExecSQLWorker',数据合同名称为“ExecSQLWorker:http://schemas.datacontract.org/2004/07/DV_BII30”。如果您正在使用 DataContractSerializer 或将任何静态未知的类型添加到已知类型列表中,请考虑使用 DataContractResolver - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给序列化程序的已知类型列表中。有关更多详细信息,请参阅 InnerException。 ---> System.Runtime.Serialization.SerializationException:不应使用数据合同名称“ExecSQLWorker:http://schemas.datacontract.org/2004/07/DV_BII30”键入“DV_BII30.ExecSQLWorker”。如果您使用 DataContractSerializer 或将任何静态未知的类型添加到已知类型列表中,请考虑使用 DataContractResolver - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给序列化程序的已知类型列表中。

你有几种可能解决这个问题。

  1. KnownTypeAttribute 添加到您的WorkerTemplate 类中

    [DataContract]
    [KnownType(typeof(ExecSQLWorker))]
    [KnownType(typeof(CopyWorker))]
    public class WorkerTemplate
    {
        //Properties and stuff
    }
    
  2. 如果可以改IPQWService接口,看看here

  3. 您可以使用DataContractResolver

    public class SharedTypeResolver : DataContractResolver
    {
        public override bool TryResolveType(Type dataContractType, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
        {
            if (!knownTypeResolver.TryResolveType(dataContractType, declaredType, null, out typeName, out typeNamespace))
            {
                XmlDictionary dictionary = new XmlDictionary();
                typeName = dictionary.Add(dataContractType.FullName);
                typeNamespace = dictionary.Add(dataContractType.Assembly.FullName);
            }
        }
    
        public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
        {
            return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? Type.GetType(typeName + ", " + typeNamespace);
        }
    }
    

    解析器接受您在服务中使用的每种类型。但是您必须将解析器添加到您的客户端端点和服务器端点。像这样:

    Host = new ServiceHost(typeof(MyService));
    
    ContractDescription cd = Host.Description.Endpoints[0].Contract;
    foreach (var operation in cd.Operations)
    {
        operation.Behaviors.Find<DataContractSerializerOperationBehavior>()
                .DataContractResolver = new SharedTypeResolver();
    }
    

【讨论】:

  • 所以虽然 WorkerTemplate 是在 DLL 中定义的,但我必须在 WCF 服务中完全重新定义它吗?就代码重复而言,这不是有点糟糕吗?
  • @tuxmania 是的,KnownTypeAttribute 有很多缺陷,但它是解决问题的最简单方法。我编辑了我的答案以显示其他方式。
  • 我将此添加到` [ServiceContract] [ServiceKnownType(typeof(ExecSQLWorker))] [ServiceKnownType(typeof(CopyWorker))] [ServiceKnownType(typeof(WorkerTemplate))] 公共接口 IPQWService { [OperationContract] void EnqueueWorker(WorkerTemplate[] worker); }` 我仍然得到同样的错误
  • @tuxmania 您需要将属性添加到WorkerTemplate 类,而不是接口。如果您想更改界面,请查看第 2 点的链接。
  • MSDN 告诉我一些不同的事情。 [ServiceKnownType] 也可以仅与类型一起使用,并且必须将其添加到作为我的接口的 ServiceContract。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-12
  • 2011-10-10
  • 1970-01-01
  • 1970-01-01
  • 2012-04-29
  • 2011-03-08
相关资源
最近更新 更多