How to dynamically consume a M3 Web Service in C#

Previous posts have dealt successfully with how to consume a web service and how to use the SmartOffice DynamicWs classes.  This post will help if you need something which is dynamic, yet decoupled from SmartOffice.

This is a sample based on the WCF Dynamic Proxy classes available under a Microsoft Public License in the msdn archive.

For initial reference we have a standard C# invocation using a generated service reference.  The web service we are using is API_MNS150MI_GetUserData.

ScreenShot2390

The only tricky part here is ensuring the http authentication is set so that Web services accepts you as a valid user.

Here is the code for the static call against the generated service reference.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using WebServiceStatic.API131;
using System.ServiceModel;

namespace WebServiceStatic
{
class Program
{
static void Main(string[] args)
{
// Create a client with basic http credentials
API_MNS150MI_GetUserDataClient client = new API_MNS150MI_GetUserDataClient();
System.ServiceModel.BasicHttpBinding binding = new System.ServiceModel.BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.MaxReceivedMessageSize = 25 * 1024 * 1024;
client.Endpoint.Binding = binding;

// show endpoint address
Console.WriteLine(client.Endpoint.Address);
Console.WriteLine(client.Endpoint.Name);

// ask for UserID and password
Console.Write("User ID : ");
client.ClientCredentials.UserName.UserName = Console.ReadLine().Trim();
Console.Write("Password: ");
client.ClientCredentials.UserName.Password = Console.ReadLine().Trim();

// Create LWS header
lws header = new lws();
header.user = client.ClientCredentials.UserName.UserName;
header.password = client.ClientCredentials.UserName.Password;

// Create a requests item
GetUserDataItem item1 = new GetUserDataItem();
item1.USID = client.ClientCredentials.UserName.UserName;

// construct a collection for the request item (only 1 accepted?)
GetUserDataCollection collection = new GetUserDataCollection();
collection.GetUserDataItem = new GetUserDataItem[] { item1 };

try
{
// execute the web service
GetUserDataResponseItem[] response = client.GetUserData(header, collection);
// loop through the response items (only 1) and output to console
foreach (GetUserDataResponseItem responseItem in response)
{
Console.WriteLine("User '{0}' description '{1}'", responseItem.USID, responseItem.TX40);
}
}
catch (Exception e)
{
// catch and display any errors
Console.WriteLine(e.Message);
}

// wait for user to press a key
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}

The result shows we have connected to the web service and retrieved the Users ID (USID) and description (TX40).

ScreenShot2391

Now we have the basic hard-coded example code as a template, we can use the DynamicProxyLibrary to do the same.

This dynamically creates an Assembly (dll) containing the service reference which we can use in place of a hard-coded Service reference.

This can be done without the DynamicProxyLibrary however as the DynamicProxy handles most of the Assembly/Reflection plumbing it is much easier to read and work with.

First create a project referencing the DynamicProxyLibrary in Visual Studio.

ScreenShot2393

Now it is possible to use the Dynamic proxy to call the web service without using a hard-coded service references, all field/property/class references can be coded as text.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WebServiceDynamic
{
using WcfSamples.DynamicProxy;
using System.ServiceModel.Description;
using System.ServiceModel;
using System.Reflection;

class Program
{
static void Main(string[] args)
{
string serviceWsdlUri = "https://m3app-2013.gdeinfor2.com:41964/mws-ws/services/API_MNS150MI_GetUserData?wsdl";
if (args.Length > 0)
serviceWsdlUri = args[0];

// create the dynamic proxy factory, that downloads the service metadata
// and create the dynamic factory.
Console.WriteLine("Creating DynamicProxyFactory for " + serviceWsdlUri);
DynamicProxyFactory factory = new DynamicProxyFactory(serviceWsdlUri);

// list the endpoints.
int count = 0;
foreach (ServiceEndpoint endpoint in factory.Endpoints)
{
// create proxy client
Console.WriteLine("Service Endpoint[{0}]", count);
Console.WriteLine("\tAddress = " + endpoint.Address);
Console.WriteLine("\tContract = " + endpoint.Contract.Name);
Console.WriteLine("\tBinding = " + endpoint.Binding.Name);
DynamicProxy clientProxy = factory.CreateProxy(endpoint.Contract.Name);

// Create a client with basic http credentials
System.ServiceModel.BasicHttpBinding binding = new System.ServiceModel.BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.MaxReceivedMessageSize = 25 * 1024 * 1024;
ServiceEndpoint clientEndpoint = (ServiceEndpoint)clientProxy.GetProperty("Endpoint");
clientEndpoint.Binding = binding;

// ask for UserID and password
ClientCredentials credentials = (ClientCredentials)clientProxy.GetProperty("ClientCredentials");
Console.Write("User ID : ");
credentials.UserName.UserName = Console.ReadLine().Trim();
Console.Write("Password: ");
credentials.UserName.Password = Console.ReadLine().Trim();

// Create LWS header
Type lwsType = clientProxy.ProxyType.Assembly.GetType("lws");
DynamicObject header = new DynamicObject(lwsType);
header.CallConstructor();
header.SetProperty("user", credentials.UserName.UserName);
header.SetProperty("password", credentials.UserName.Password);

// Create a requests item
Type itemType = clientProxy.ProxyType.Assembly.GetType("GetUserDataItem");
DynamicObject item = new DynamicObject(itemType);
item.CallConstructor();
item.SetProperty("USID", credentials.UserName.UserName);

// Add the user request item to an array of 1
Array itemArray = Array.CreateInstance(item.ObjectType, 1);
itemArray.SetValue(item.ObjectInstance, 0);

// construct a collection for the request item (only 1 accepted?)
Type collectionType = clientProxy.ProxyType.Assembly.GetType("GetUserDataCollection");
DynamicObject collection = new DynamicObject(collectionType);
collection.CallConstructor();
collection.SetProperty("GetUserDataItem", itemArray);

try
{
// execute the web service
Array responseCollection = (Array)clientProxy.CallMethod("GetUserData", new object[] { header.ObjectInstance, collection.ObjectInstance });
// loop through the response items (only 1) and output to console
foreach (object responseItemObject in responseCollection)
{
DynamicObject responseItem = new DynamicObject(responseItemObject);
Console.WriteLine("User '{0}' description '{1}'",
responseItem.GetProperty("USID"),
responseItem.GetProperty("TX40"));
}
}
catch (Exception e)
{
// catch and display exceptions
Console.WriteLine(e.Message);
// catch and display inner exception (this is the real error from the web service call)
if (e.InnerException != null)
{
Console.WriteLine(e.InnerException.Message);
}
}
// close the connection
clientProxy.Close();
}

Console.WriteLine("Press any key...");
Console.ReadKey();
}

}
}

The result shows that we can use the DynamicProxyFactory to get some basic information about the web service, then consume the web service.

ScreenShot2392

Regards,

Lee Flaherty

UPDATE: This was tested against M3 10.1 and M3 13.1 both running on the grid.  It may, or may not, work on other versions.