Reputation: 85
Let me first describe what my intention is, then go into my question.
I’m trying to build a system that communicates to a SOAP service where the SOAP request is somewhat unknown at runtime. Ultimately what I need to do is generate a SOAP request from unknown objects. I’ll be dynamically creating objects from scratch with the appropriate attributes and properties, then pass that to my service “request” method to be sent to the SOAP service. Here is the code I’ve been working with and then the error I’m receiving.
SOAP Client:
/// <summary>
/// GOSService proxy class
/// </summary>
[DebuggerStepThroughAttribute()]
[DesignerCategoryAttribute("code")]
[WebServiceBindingAttribute(Name = "ServiceSoapBinding", Namespace = "service.domain.com", ConformsTo = WsiProfiles.None)]
[SoapRpcService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
public partial class TestService : SoapHttpClientProtocol
{
/// <summary>
/// Initializes a new instance of the TestService class.
/// </summary>
public TestService()
{
this.Url = "https://restsv01.domain.com/ServiceTest/services/TestService";
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnRemoteCertificateValidationCallback);
}
/// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication.
/// </summary>
/// <param name="sender">An object that contains state information for this validation.</param>
/// <param name="certificate">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote certificate.</param>
/// <returns>A Boolean value that determines whether the specified certificate is accepted for authentication.</returns>
private bool OnRemoteCertificateValidationCallback(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
/// <summary>
///
/// </summary>
/// <param name="order"></param>
/// <returns></returns>
[SoapRpcMethodAttribute("", RequestNamespace = "service.domain.com", ResponseNamespace = "service.domain.com")]
[SampleSoap.LoggerSoapExtensionAttribute]
[return: SoapElementAttribute("requestReturn")]
public object request(object parm)
{
object[] results = this.Invoke("request", new object[] { parm });
return ((object)(results[0]));
}}
Test Model: (There won't be any pre-defined models, they will be dynamically generated. But for testing purposes, I'm using this model to test with)
[SerializableAttribute()]
[DebuggerStepThroughAttribute()]
[DesignerCategoryAttribute("code")]
[SoapTypeAttribute(Namespace = "http://entity.domain.com")]
public class ParentNode
{
private string nameField = "1";
[SoapElementAttribute(IsNullable = true)]
public string Name
{
get { return this.nameField; }
set { this.nameField = value; }
}
}
Test Call Code:
Services.Soap.Models.ParentNode parent = new Services.Soap.Models.ParentNode();
parent.Name = "John Doe";
Services.Soap.TestService service = new Services.Soap.TestService();
object resp = service.request(parent);
When I run this code, an error occurs at this line:
object[] results = this.Invoke("request", new object[] { parm });
This is the error:
The type Services.Soap.Models+ParentNode was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
Now, if I change the parameter of the service “request” method to a strong type, the request builds fine and is passed to the SOAP service, like so.
public object request(ParentNode parm)
I’ve tried probably 50 things to get this to work, including passing the Type as a parameter to the request method and creating a “dynamic” instance of the object to pass.
public object request(object parm, Type t)
{
dynamic converted = Convert.ChangeType(parm, t);
object[] results = this.Invoke("request", new object[] { converted });
return ((object)(results[0]));
}
This didn’t work because “converted” was still considered an object type.
I’ve also tried intercepting the soap envelope in the “GetWriterForMessage” method so I can build my own envelope, but I wasn’t able to make use of that.
So my question is, how can I get the SOAP request to build successfully with a parameter type of object? Is there maybe another approach I should take to make my architecture work correctly?
Upvotes: 1
Views: 2998
Reputation: 12211
Your description of the system you are implementing is not quite clear to me. You mention that you will send "dynamic" requests to a SOAP server? Or are you creating some service with "dynamic" operations. Those details are a little muddy for me however I scribbled down some thoughts.
In short you are opening a world of pain for yourself if you want to do this in SOAP. REST might be a better fit as it does not have a WSDL but if you are using WADL, RAML and these emerging standards your milage may vary. I am not going to try and even answer the technical question for you but try to make you see why this is a one way ticket to maintenance hell.
This implementation really breaks SOA concepts(particularly Standardized service contract), which might not be applicable in your case as just implementing a dynamic WSDL with unknown content disqualifies the architecture as NOT SOA. This also contradicts the whole point of having a WSDL. The WSDL is a interface definition in XML. Thus dynamic service would require a dynamic interface which then qualifies it as NOT an interface. Thus when you use the object type as a parameter the WSDL and interface does not match up.
When other system consume your SOAP services they will do so based on a published WSDL. If the WSDL changes their programs will break as the definition or WSDL does not match the data types they have generated. A service that works on HTTP and gets requests and sends replies and that does not have a WSDL is NOT a SOAP service it is a XML based service using HTTP ports. They are fundamentally different. Does your service provider i.e. SOAP service you are requesting data from not have a WSDL? If not these are not the droids you are looking for :)
An interface is a agreed upon contract that applications can use to query a service. Essentially you are talking about structured vs unstructured data objects(at least at run time).
If you want this type of pattern you need to look at a publish subscribe architecture something like MQTT which is lightweight to use, or another example would be JMS with topics.
If you are consuming a web service which you mention there WILL be a WSDL which your .Net application can use to generate the datatypes and associated C# classes. The classes will include both the data being transmitted and the operations exposed on the service.
In official SOAP worlds there is no such thing as a dynamic service as it breaks the whole paradigm of we agree as server and client to communicate via this form of XML during operations.
Upvotes: 1