I’m completely new to C# and trying to implement a client for an RO DataAbstract SOAP service. How do I set the ROClientID? Here’s my code so far:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Create a new instance of the login service
MyLibrary.LoginServiceClient LoginClient = new MyLibrary.LoginServiceClient();
// Call the Login method on the server
MyLibrary.TLoginInfo LoginInfo = LoginClient.Login("Admin", "master");
// Set the Session ID
var SessionID = LoginInfo.SessionId;
// Create a new instance of the Utils service
MyLibrary.UtilsClient UtilsClient = new MyLibrary.UtilsClient();
// Set the ROClient ID???????
// How do i set the ROClientID for the next call ????????????????
// Get the Add Result
var IPVersion = UtilsClient.AddValues(1, 1);
}
}
I’ve tried your suggestion. It looks like it will do the job, but due to my lack of C# knowledge I can’t get it to work (The SoapExtention methods are never called). Can you please take a look and tell me how I ‘attach’ the handlers to the SOAP calls?
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Xml;
using System.Windows.Forms;
using System.Web.Services.Protocols;
namespace MySOAPDemo
{
public partial class MainForm : Form
{
string FSessionID;
public MainForm()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (btnLogin.Text.Equals("Login"))
Login();
else
Logout();
}
private void Login()
{
try
{
// Create a new instance of the login service
MyLibrary.LoginServiceClient LoginClient = new MyLibrary.LoginServiceClient();
LoginClient.Endpoint.Address = new System.ServiceModel.EndpointAddress(edtURL.Text);
// Call the Login method on the server
MyLibrary.TLoginInfo LoginInfo = LoginClient.Login(edtUsername.Text, edtPassword.Text, 0, 0, MyLibrary.TClientType.ctWindowsClient);
// Set the Session ID
FSessionID = LoginInfo.SessionId;
btnLogin.Text = "Logout";
lblStatus.Text = "Connected - Session ID = " + FSessionID;
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void Logout()
{
MyLibrary.LoginServiceClient LoginClient = new MyLibrary.LoginServiceClient();
LoginClient.Endpoint.Address = new System.ServiceModel.EndpointAddress(edtURL.Text);
LoginClient.Logout();
btnLogin.Text = "Login";
lblStatus.Text = "Not Connected";
}
private void btnUtils_Click(object sender, EventArgs e)
{
try
{
MyLibrary.MyUtilsClient UtilsClient = new MyLibrary.MyUtilsClient();
UtilsClient.Endpoint.Address = new System.ServiceModel.EndpointAddress(edtURL.Text);
string[] NewLines = new string[2];
NewLines[0] = UtilsClient.PadIPAddress(edtUtilsSubnet.Text);
NewLines[1] = UtilsClient.GetSubnet(edtUtilsSubnet.Text, int.Parse(edtUtilsCIDRMask.Text));
memUtilsResults.Lines = NewLines;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
public class myExtension : SoapExtension
{
public bool outgoing = true;
public bool incoming = false;
private Stream outputStream;
public Stream oldStream;
public Stream newStream;
public override void Initialize(object initializer)
{
}
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return "";
}
public override object GetInitializer(Type WebServiceType)
{
return "";
}
public override Stream ChainStream(Stream stream)
{
// save a copy of the stream, create a new one for manipulating.
this.outputStream = stream;
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
public string getXMLFromCache()
{
newStream.Position = 0; // start at the beginning!
string strSOAPresponse = ExtractFromStream(newStream);
return strSOAPresponse;
}
private String ExtractFromStream(Stream target)
{
if (target != null)
return (new StreamReader(target)).ReadToEnd();
return "";
}
public override void ProcessMessage(SoapMessage message)
{
StreamReader readStr;
StreamWriter writeStr;
string soapMsg1;
XmlDocument xDoc = new XmlDocument();
// a SOAP message has 4 stages. We're interested in .AfterSerialize
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
{
// Process the code here
break;
}
case SoapMessageStage.BeforeDeserialize:
{
// Make the output available for the client to parse...
readStr = new StreamReader(oldStream);
writeStr = new StreamWriter(newStream);
soapMsg1 = readStr.ReadToEnd();
xDoc.LoadXml(soapMsg1);
soapMsg1 = xDoc.InnerXml;
writeStr.Write(soapMsg1);
writeStr.Flush();
newStream.Position = 0;
break;
}
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalid stage!");
}
}
}
}
Sorry, this will not work for us. We have a product that is used by hundreds of customers. They need to connect to the existing service, not a new one with a completely different API.
In addition, we have 100s of methods in across several services. It is not practical to add a session ID field to every call.
It is trivial to get this working in Delphi. I can’t believe it is this hard to add a new header to a SOAP call in C#.
Create two custom header classes that derived from SoapHeader as follows:
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://tempuri.org/")]
[System.Xml.Serialization.XmlRootAttribute("ROClientIDHeader", IsNullable = false)]
public class ROClientIDHeaderHeader : SoapHeader
{
public IDHeader ID = new IDHeader();
}
[System.Xml.Serialization.XmlRootAttribute(“ID”, IsNullable = false)]
public class IDHeader : SoapHeader
{
[System.Xml.Serialization.XmlTextAttribute()]
public string IDValue;
}
Corresponding objects will be used to create custom SOAP Header:
{72F2025B-8E86-42D3-BE18-E4C75E1DEF2F} //ClientID
2. Create an instance of the custom header in the generated service client.
public partial class ROServer62ServiceService : System.Web.Services.Protocols.SoapHttpClientProtocol {
{
public ROClientIDHeaderHeader creds;
. . .
}
Refer to the custom header before each call that requires the custom header:
[System.Web.Services.Protocols.SoapHeaderAttribute("creds", Direction = System.Web.Services.Protocols.SoapHeaderDirection.InOut)] //Need to add
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("urn:ROServer62Library-ROServer62Service#Sum", RequestElementName="ROServer62Service___Sum", RequestNamespace="http://tempuri.org/", ResponseElementName="ROServer62Service___SumResponse", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("Result")]
public int Sum(int A, int B) {
. . .
}
Call the modified service as follows:
ROClientIDHeaderHeader customSoapHeader = new ROClientIDHeaderHeader();
mySoapHeader.ID.IDValue = SessionGuid;
ROServerService.ROServer62ServiceService service = new ROServer62ServiceService();
service.creds = customSoapHeader;
var res = service.Sum(1, 2);
Please take a look at the attachment archive. It shows what I have said above in action:
“Create two custom header classes that derived from SoapHeader as follows”. Please take a look at Form1.cs file. Corresponding SoapHeaders are declared there. They could be declared everywhere in the client project.
“Corresponding objects will be used to create custom SOAP Header:” - corresponding objects are created manually and described in step 4.
“Create an instance of the custom header in the generated service client.”. Please take a look at Reference.cs file for ROServer62ServiceService.
public ROClientIDHeaderHeader creds; is defined for public partial class ROServer62ServiceService
“Refer to the custom header before each call that requires the custom header”. Please take a look at Reference.cs file for ROServer62ServiceService. For each service method SoapHeaderAttribute is defined:
[System.Web.Services.Protocols.SoapHeaderAttribute("creds", Direction = System.Web.Services.Protocols.SoapHeaderDirection.InOut)]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("urn:ROServer62Library-ROServer62Service#Sum", RequestElementName="ROServer62Service___Sum", RequestNamespace="http://tempuri.org/", ResponseElementName="ROServer62Service___SumResponse", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("Result")]
public int Sum(int A, int B) {
[System.Web.Services.Protocols.SoapHeaderAttribute("creds", Direction = System.Web.Services.Protocols.SoapHeaderDirection.InOut)]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("urn:ROServer62Library-ROServer62Service#GetServerTime", RequestElementName="ROServer62Service___GetServerTime", RequestNamespace="http://tempuri.org/", ResponseElementName="ROServer62Service___GetServerTimeResponse", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("Result")]
public System.DateTime GetServerTime() {
You need to alter generated client code manually.
“Call the modified service as follows”. Please take a look at Form1.cs file.