This article about SOAP(client) is actually platform-specific. But programming in C# is very popular and is a big part of .NET I feel it's important to learn/get a hang of C# and its way of handling the SOAP-service. It's been a long time since the last time I used Visual Studio, but I remember it as being a very nice experience. I used to do some programming in Visual C++ and the DirectX interface. I think it was DirectX 8..or 9..using Direct3D/DirectSound and so on...but this is a digression...The point of this article is to connect to a server using the SOAP-protocol via a WSDL. I think using the WSDL is the more simple way, and it's easy to use. Must admit that this is the first time I appoach C#. I went through a "basics"-tutorial and found out that the syntax and language was quite like C, but also a little bit like Java(even though I do not have a lot of experience programming Java).
Like the other articles I've written I will do two operations:
1. Importing the WSDL services to your project
But first of all we have to import the available services/methods to our project. My project is a simple console application.
1. I create the project/Solution through "Visual C#" -> Console App (.NET Framework).
2. Right-Click on "References" in the "Solution Explorer" pane on the right and click on the "Add Service Reference". See image below:
3. In the Dialog Box that appears, enter the wsdl adresse that you want top connect to. After that click "Go". The VS will then connect to that adresse and collect/get all available services/methods. When everything is downloaded/scanned create a neamespace name for this which you will use in your project and click "OK". See pic below:
4. After clicking "OK", you will be back at your source-file, but on the right-pane you will see a new connected service that is available. See pic below:
5. Double-click the "myNamespaces"-line and a new tab will open up in your main window showing the classes and its methods and member variables. See pic below:
When this is "a-ok" we're ready to start making an "app"/connecting to the server.
2. Getting Order-Information/Using the WSDL-Interface.
It was really interesting doing this in C# since I've never programmed in C#. But it was not a problem since the syntax and the source layout is quite like C/C++ or PHP. What was new for me was the usage of namespaces. But not a problem.
1. The first thing we have to do is to tell the program that we're using the new service that's been downloaded/created. See line 6:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using soapGetOrder.OnGoing; namespace soapGetOrder { class TestO { private const string password = "password"; private const string username = "UserName"; private const string id = "goods_owner_id"; private const string oid = "goodsowner_order_number"; public int Get_order_by_id()
- SoapGetOrder points to this programs' namespace.
- OnGoing points to the name of the service that we created earlier on. See the Solution Explorer under "Connected Services"
In my class TestO I create a public function that will connect to the service and get order-information. See code below:
public int Get_order_by_id() { Order OrderResponse; ServiceSoapClient testClient = new ServiceSoapClient(); OrderResponse = testClient.GetOrderByOrderNumber(id, username, password, oid); if(!OrderResponse.Success) { Console.WriteLine("OrderResponse.Success returned false. Could not get order"); return 1; } else {
- Line 3: Create a variable that will hold the Order.
- Line 4: Craete an instance of the ServiceSoapClient. This variable can actually be made implicitly if you want. It can also be overloaded. Some of the params that can be added is endpoint-configuration-name and remote adresse if it's needed. Here we do not need them.
- Line 5: Get the order by using the member-function GetOrderByOrderNumber and assign result to the OrderResponse variable.
And that's it! If the operation was successfull OrderResponse will contain all the orderinformation that's available. I tested for some information that i wrote into the console. See code below:
else { Console.WriteLine("Getting some information from the OrderInfo-object:"); Console.WriteLine("Getting the order was a success. Testing som variables."); Console.WriteLine("OrderInfo->OrderId: {0}", OrderResponse.OrderInfo.OrderId); Console.WriteLine("OrderInfo->Status: {0}", OrderResponse.OrderInfo.OrderStatusText); Console.WriteLine("OrderInfo->WayOfDelivery: {0}", OrderResponse.OrderInfo.WayOfDeliveryTypeName); Console.WriteLine("OrderInfo->OrderRemark: {0}\n\n", OrderResponse.OrderInfo.OrderRemark); Console.WriteLine("Total number of Items on order\"{0}\"", OrderResponse.OrderInfo.OrderedNumberOfItems); Console.WriteLine("Total number of Picked Items \"{0}\"", OrderResponse.OrderInfo.PickedNumberOfItems); TransporterClass tc = OrderResponse.Transporter; Console.WriteLine("\nTransporter info: \nName: {0}\nCode: {1}\nServiceCode: {2}\nTransporterName: {3}", tc.Name, tc.Code, tc.ServiceCode, tc.TransporterName); //total number of order-lines int NumberOfOrderLines = OrderResponse.PickedOrderLines.Length; //Get ordlines in order if (NumberOfOrderLines > 0) { Console.WriteLine("Items in Order: "); for (int i = 0; i < NumberOfOrderLines; i++) { Console.WriteLine("Number of items: {0} of {1}", OrderResponse.PickedOrderLines[i].OrderedNumberOfItems, OrderResponse.PickedOrderLines[i].Article.ArticleNumber); } } else Console.WriteLine("No orderlines in order"); //Total number of picked orderlines int NumberOfPickedArticleItems = OrderResponse.PickedArticleItems.Length; if (NumberOfPickedArticleItems > 0) { Console.WriteLine("Picked items: "); for (int i = 0; i < NumberOfPickedArticleItems; i++) { Console.WriteLine("Picked {2} of Article: {1}. Article name: {0}", OrderResponse.PickedArticleItems[i].Article.Name, OrderResponse.PickedArticleItems[i].Article.ArticleNumber, OrderResponse.PickedArticleItems[i].NumberOfItems); } } else Console.WriteLine("Nothing is picked yet"); }
Result of this is the following:
Getting some information from the OrderInfo-object: Getting the order was a success. Testing som variables. OrderInfo->OrderId: 21696 OrderInfo->Status: Skrevet ut OrderInfo->WayOfDelivery: OrderInfo->OrderRemark: mobil: 97501600-Test xml->obj->send request using gSOAP----FM Total number of Items on order"6,000" Total number of Picked Items "3,000" Transporter info: Name: PostNord NO MyPack Collect Code: DTPG ServiceCode: P19NO TransporterName: PostNord Logistics AS (Norge) Items in Order: Number of items: 3,000 of 101213 Number of items: 1,000 of 101363 Number of items: 2,000 of 101502 Picked items: Picked 2,000 of Article: 101213. Article name: ProSmart varmefolie 60w/m2- 1,2x3,15m Picked 1,000 of Article: 101502. Article name: Tilkoblingsledning 3m
In the OrderInfo->OrderRemark it mentions gSOAP. This order was created with the gSOAP API. See Talking SOAP with C/C++
3. Creating an Order (from a xml-file)
Now I'm going to create an order from a datafile in xml-format. This file has not been given any namespace. Here's an excerpt:
<?xml version="1.0" encoding="utf-8" ?> <CustomerOrder> <OrderInfo> <OrderIdentification>GoodsOwnerOrderNumber</OrderIdentification> <OrderOperation>CreateOrUpdate</OrderOperation> <GoodsOwnerOrderNumber>201904162010-test</GoodsOwnerOrderNumber> <ReferenceNumber>10077072</ReferenceNumber>
To fill our CustomerOrder-object with data we're goint to have to deserialize data in the file to. We use the Class XmlSerializer. This class has a function that can deserialize a stream.
But I had a problem, which I did not foresee. Visual Studio sets an order-variable that locks all data in a certain order/sequence. In other words: the data in the xml-file has to be in a certain order. This is not a problem if another process serializes the data in the correct sequence/order, but it would be nice NOT to be coerced to this specific order.
What we do to solve this, is that we create our own class to hold the data from the file. To do this we do the following:
1. Select all data in the file.
2. Press 'Ctrl-C' (copy the data)
3. Press Edit->'Paste Special'->Paste XML as Classes in Visual Studio. See pic below:
Visual Studio will now create a Class based on the xml-data that can serialize. Rename This class to something other than what Visual Studio created earlier from the WSDL. I called mine for 'CustomerOrderFromFile'. Now we can deserialize the xml-data. See code below:
string path = @"path_to_\create_order.xml"; var ots = new CustomerOrderFromFile.CustomerOrder(); XmlSerializer serializer = new XmlSerializer(typeof(CustomerOrderFromFile.CustomerOrder)); StreamReader reader = new StreamReader(path); ots = (CustomerOrderFromFile.CustomerOrder)serializer.Deserialize(reader); reader.Close();
The next thing to do is to set the data in the original CustomerOrder class. This is alot of work but it's worth it so that we are not locked to the specific reading order of the data. I made a function that does that. Here's an excerpt:
private int coffToServiceCo(CustomerOrderFromFile.CustomerOrder coff) { var co = new CustomerOrder(); //Original CustomerOrder class //map/insert OrderInfo co.OrderInfo = new OrderInfoClass(); var cooi = co.OrderInfo; cooi.OrderIdentification = (OrderIdentificationType)Enum.Parse(typeof(OrderIdentificationType), coff.OrderInfo.OrderIdentification); cooi.OrderOperation = (OrderOperation)Enum.Parse(typeof(OrderOperation), coff.OrderInfo.OrderOperation); cooi.GoodsOwnerOrderNumber = coff.OrderInfo.GoodsOwnerOrderNumber; cooi.ReferenceNumber = coff.OrderInfo.ReferenceNumber.ToString(); cooi.OrderRemark = coff.OrderInfo.OrderRemark; cooi.SalesCode = coff.OrderInfo.SalesCode.ToString(); cooi.DeliveryDate = coff.OrderInfo.DeliveryDate; cooi.OrderStatusCreated = coff.OrderInfo.OrderStatusCreated; cooi.OrderStatusUpdated = coff.OrderInfo.OrderStatusUpdated; //map/insert the goodsowner-class variables co.GoodsOwner = new GoodsOwner(); var cogo = co.GoodsOwner; cogo.GoodsOwnerIdentification = (GoodsOwnerIdentificationType)Enum.Parse(typeof(GoodsOwnerIdentificationType), coff.GoodsOwner.GoodsOwnerIdentification); cogo.GoodsOwnerId = coff.GoodsOwner.GoodsOwnerId; -------------- Etc ------------------------
For the OrderLines I do this:
//here comes the orderlines... var mappedLines = new List<CustomerOrderLine>(); foreach (var fileLine in coff.CustomerOrderLines) { var mappedLine = new CustomerOrderLine(); mappedLine.OrderLineIdentification = (OrderLineIdentificationType)Enum.Parse(typeof(OrderLineIdentificationType), fileLine.OrderLineIdentification); mappedLine.ArticleIdentification = (ArticleIdentificationType)Enum.Parse(typeof(ArticleIdentificationType), fileLine.ArticleIdentification); mappedLine.ArticleNumber = fileLine.ArticleNumber.ToString(); mappedLine.NumberOfItems = fileLine.NumberOfItems; mappedLines.Add(mappedLine); } co.CustomerOrderLines = mappedLines.ToArray(); this.custO = co; //Assign the data to our-classes member-variable of type CustomerOrder
The next thing to do is to send the order. I made a function called sendOnGoingOrder():
public int sendOnGoingOrder() { ServiceSoapClient Oh = new ServiceSoapClient(); FileResultClass frc = new FileResultClass(); frc = Oh.ProcessOrder(coid, username, password, this.custO); if (frc.Success) { Console.WriteLine("Inserting the order was a success"); Console.WriteLine("Message: ", frc.Message); Console.WriteLine("OrderId = {0}", frc.OrderId); Console.WriteLine("GoodsOwnerORderNumber = {0}", frc.GoodsOwnerOrderNumber); } else { Console.WriteLine("ErrorMessage: {0}", frc.ErrorMessage); return 1; //program should quit with an error-number...1 } return 0; }
The output of this is:
OrderInfo->OrderRemark SOAP/WSDL test using Visual Studio Community 2017 using C# - CreateOrder----FM GoodsOowner->GoodsOwnerId 67 TransporterContract-> TransporterCode DTPG CustomerOrderlines[0]->ArticleNumber 101213 Inserting the order was a success Message: OrderId = 21777 GoodsOwnerORderNumber = 201904181612-test