WCF - 传递未声明为 ServiceKnownType 的对象
我有以下通过 net.tcp 公开的 WCF 接口:
[ServiceContract]
public interface IMyWCFService
{
[OperationContract]
Response ProcessRequest(Request request);
}
这是由以下类驱动的(为了解决此问题而进行了极大简化):
[Serializable]
public abstract class Message
{
[XmlAttribute]
public string Sender { get; set; }
[XmlAttribute]
public string Recevier { get; set; }
}
[Serializable]
public abstract class Response : Message
{
[XmlAttribute]
public int EventCode { get; set; }
}
[Serializable]
public abstract class Request : Message
{
[XmlAttribute]
public string SourceSystem { get; set; }
}
[XmlRoot(Namespace="http://blah.blah.com/blah/")]
public class StringRequest : Request
{
[XmlElement]
public string Payload { get; set; }
}
[XmlRoot(Namespace="http://blah.blah.com/blah/")]
public class StringResponse : Response
{
[XmlElement]
public string Payload { get; set; }
}
注意:我们使用 XMLSerializer
而不是 < code>DataContractSerializer 因为这些类必须与基于 .NET 2 的遗留系统兼容。
由于接口在 ProcessRequest 方法中使用抽象 Request/Response 类,我们必须将 StringResponse / StringRequest 声明为ServiceKnownType
,例如:
[ServiceContract]
[ServiceKnownType(typeof(StringRequest))]
[ServiceKnownType(typeof(StringResponse))]
public interface IMyWCFService
{
[OperationContract]
ResponseMessage ProcessRequest(RequestMessage request);
}
这工作得很完美,世界上一切都很好,但是......
WCF 侦听器只是一个更大框架的一个组件,并且始终使用上述类。我们还设计了框架,使我们能够相对轻松地添加新类型的请求/响应消息。例如,我可能会添加:
public class CustomRequest : Request
{
public MyCustomXmlSerialisableRequestObject Payload { get; set; }
}
public class CustomResponse: Response
{
public MyCustomXmlSerialisableResponseObject Payload { get; set; }
}
在我获得 WCF 服务接口之前,这也可以正常工作。当我们添加新的自定义请求/响应对时,我们还需要更新接口上的 ServiceKnownType 以包含它们。这意味着我必须重新部署该服务。所以问题是 - 有什么方法可以避免更新界面吗?
举个例子,当我们使用远程处理时,我们可以传递任何我们喜欢的对象,只要它们是可序列化的,所以我假设/希望 WCF 中有一个类似的解决方案。
编辑:更新
遵循此处的指导:
http://ashgeek.blogspot.com/2011/02/wcf-serialization-dynamically-add.html
我似乎走在正确的轨道上。但是,当我更新客户端服务引用时,它将所有动态类型拉入服务引用中。这是不可取的,因为并非所有客户端都需要或应该了解从请求/响应派生的所有消息
更重要的是,我似乎丢失了用于推送消息的 ServiceClient 类,例如:
// Client proxy class goes AWOL after service reference update
var client = new MyServiceReference.Client();
var responseMessage = client.ProcessRequest(requestMessage)
I have the following WCF interface that is exposed via net.tcp:
[ServiceContract]
public interface IMyWCFService
{
[OperationContract]
Response ProcessRequest(Request request);
}
This is driven by the following classes (much simplified for the purposes of this question):
[Serializable]
public abstract class Message
{
[XmlAttribute]
public string Sender { get; set; }
[XmlAttribute]
public string Recevier { get; set; }
}
[Serializable]
public abstract class Response : Message
{
[XmlAttribute]
public int EventCode { get; set; }
}
[Serializable]
public abstract class Request : Message
{
[XmlAttribute]
public string SourceSystem { get; set; }
}
[XmlRoot(Namespace="http://blah.blah.com/blah/")]
public class StringRequest : Request
{
[XmlElement]
public string Payload { get; set; }
}
[XmlRoot(Namespace="http://blah.blah.com/blah/")]
public class StringResponse : Response
{
[XmlElement]
public string Payload { get; set; }
}
Note : We use XMLSerializer
rather than DataContractSerializer
as these classes have to be compatible with legacy systems that are .NET 2 based.
As the interface uses the abstract Request/Response classes in the ProcessRequest method we have to declare StringResponse / StringRequest as ServiceKnownType
, for example:
[ServiceContract]
[ServiceKnownType(typeof(StringRequest))]
[ServiceKnownType(typeof(StringResponse))]
public interface IMyWCFService
{
[OperationContract]
ResponseMessage ProcessRequest(RequestMessage request);
}
This works perfectly and all is good in the world, however.....
The WCF listener is just one component of a much larger framework and the classes described above are used throughout. We have also designed the framework to allow us to add new types of Request/Response messages with relative ease. For example I might add:
public class CustomRequest : Request
{
public MyCustomXmlSerialisableRequestObject Payload { get; set; }
}
public class CustomResponse: Response
{
public MyCustomXmlSerialisableResponseObject Payload { get; set; }
}
Which also works fine until I get the the WCF service interface. When we add a new custom request/response pair we also need to update the ServiceKnownType on the interface to include them. Which then means I have to redeploy the service. So the question is - is there any way I can avoid having to update the interface?
As an example when we used remoting we could pass through any objects we liked as long as they were serialisable so I assume/hope that there is a similar solution in WCF.
EDIT : Update
Following the guidance found here:
http://ashgeek.blogspot.com/2011/02/wcf-serialization-dynamically-add.html
I seem to be on the right track. However when I update the client service reference it pulls in all the dynamically types into the service reference. Which is undesirable as not all clients need to, or should, know about all messages that derive from Request/Response
More importantly I seem to lose the the ServiceClient class that is used to push messages, e.g:
// Client proxy class goes AWOL after service reference update
var client = new MyServiceReference.Client();
var responseMessage = client.ProcessRequest(requestMessage)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
一开始您提到您需要与 .NET 2.0 服务兼容,但同时您又抱怨在 .NET 远程处理中有效的某些功能在 WCF 中不起作用 - 您受到 .NET 2.0 Web 服务可能提供的功能的限制服务器和客户端都必须了解服务层上传输的类型 = 类型必须位于服务描述和 WSDL 中。此外,由于您决定使用 XmlSerializer,您通常会失去实现这一目标的大部分方法:
ServiceKnowType
可以从静态方法加载已知类型DataContractSerializer
)DataContractSerializer)
使用
XmlSerializer
,您有一个选择返回-在.NET 2.0 中不起作用XElement
并在服务操作中接收XElement
并自行处理 XML编辑:
服务描述中没有动态行为。它仅在主机启动时创建一次,之后不会更改,直到重新启动主机为止。如果您需要每个客户端的 WSDL 子集,则需要为每个客户端提供单独的端点,并且您必须准确定义应在每个端点上公开哪些数据协定。
At the beginning you are mentioning that you need compatibility with .NET 2.0 services but in the same time you are complaining that something which worked in .NET remoting doesn't work in WCF - you are limited by features possible with .NET 2.0 web services where both server and client must know about transferred types on the service layer = types must be in service description and WSDL. Moreover because you decided to use XmlSerializer you generally lost most of the ways how to achieve that:
ServiceKnowType
can load known types from static methodDataContractSerializer
)DataContractSerializer
)NetDataContractSerializer
and custom behavior) = generally this is the same functionality as in remoting and it demands sharing types between service and client and both service and client must be .NET application using WCF stuff.With
XmlSerializer
you have one optionReturn- doesn't work in .NET 2.0XElement
and receiveXElement
in your service operation and deal with XML by yourselvesEdit:
There is no dynamic behavior in service description. It is created only once when the host starts and after that doesn't change until you restart the host. If you need subset of WSDL per client you need separate endpoint for each client and you must define exactly which data contracts should be exposed on each endpoint.