Generic Methods in Non-Generic Classes in c#

I’ve been using generic classes quite a bit over the last few years. However, I had never used a generic method in a non-generic class until today. I had a web service class that was using declaritive attributes to generate the WSDL. I already had a generic class, WebMethod<R>, that I was using as the base class of all my web methods to encapsulate common functionality such as authentication, logging, etc. There were some things that I needed to in the web service class that I did not want to move to the generic web method class. However, I did not want to duplicate that code in each web method of the web service. Here’s the solution, a generic method in a non-generic class. This code has been simplified for demo purposes:

[WebService(Namespace="http://blah.com/SomeWebServices/",
  Description="Some web service.")]
public class SomeWebService : System.Web.Services.WebService {
	public R Run<R>(WebMethod<R> method) where R : IWebServiceResponse, new() {
		method.UserHostAddress = this.Context.Request.UserHostAddress;
		if (AppConfig.GetSetting("systemFactoryType").ToLower() == "local") {
			method.SystemFactory = new LocalSystemFactory();
		}
		return method.Run();
	}

	[WebMethod(Description = "Take Action.")]
	public ActionResponse Action(UserToken user, ActionRequest request) {
		return Run(new ActionWebMethod(user, request));
	}

	[WebMethod(Description = "Fiddle.")]
	public FiddleResponse Fiddle(UserToken user, FiddleRequest request) {
		return Run(new FiddleWebMethod(user, request));
	}
}

public abstract class WebMethod<R> where R : IWebServiceResponse, new() {
	public SystemFactory SystemFactory {
		set { _systemFactory = value; }
	}

	protected abstract void RunMethod();

	public R Run() {
		_response = new R();
		if (Login()) {
			RunMethod();
		}
		return _response;
	}
}

WebMethod initializes SystemFactory to a remoted implementation in the constructor. I wanted to be able to change that to a non-remoted implementation via the web.config. Similarly, I did not want to add Host Address to the WebMethod constructor, but when I am actually calling the web methods from the web service, I do want to record the Host Address. Without the generic method, I would end up duplicating this code in the body of each web method.

Note that I do not have to explicitly provide a type when I am calling Run, the compiler infers it for me.

PHP 5 Web Service Client

Until yesterday, I’d never written a single line of PHP. In fact, if you’ve read much of this blog you’ll see that most of what I do is c# and SQL Server related. Many of my posts are an attempt to simplify something that ought to have been easy but turned out to take 5 or 10 times as long as I thought it would. That was definitely the case with PHP and web services. Below are the steps that take you from a blank slate (PHP-wise) to successfully calling a .net web service using complex and composited types. In case you’re wondering why I wrote a web service client in PHP, a client was having problems accessing our web service using PHP.

My first step was to acquire and install the Uniform Server. I installed from UniServer3_5.exe. Uniform Server would have worked great if I didn’t already have IIS running on port 80. I’ve done a normal Apache install before under these circumstances, and Apache very kindly tells you the problem and how to fix it. Unfortunately, Uniform Server hides these problems. Basically, it doesn’t work and you have no idea why. If you do need to change the port, you’ll need to edit the http.conf under ..\udrive\usr\local\apache2\conf. “..\” refers to the directory where you extracted the Uniform Server (it can be anywhere you like). Search on 80 and replace with 8080 or whatever you want. The three lines I changed were:

Listen 8080
ServerName localhost:8080
<VirtualHost *>
	ServerName localhost:8080

Note that even after doing this, Start_Server will still try to take you to a URL without the port specified.

Next, you will need install and enable the Soap extension which you can find in here as ext\php_soap.dll. Copy this file to ..\udrive\usr\local\php\extensions. Now you will need to enable the extension by adding the following line to your ..\udrive\usr\local\php\php.ini:

extension=php_soap.dll

Save the changes and then stop and start the server using the supplied batch files or your changes won’t take effect.

Now we can write some code. I created my files using UltraEdit. I’m sure there are some great PHP IDEs out there but I wasn’t familiar with any. To run a php program, you simply create a file in the ..\udrive\www directory ending with the .php extension and then point your browser to http://localhost:8080/file.php. Note that you can omit the port in the URL if you are using the standard port setting of 80. In IE 7, I needed to hit ctrl-F5 to rerun my page after I had saved changes.

If you’ve looked at some other resources, keep in mind that we are using the built-in PHP soap support in PHP 5, not the older, uglier things like NuSoap, etc. The latest, built-in web service support works from wsdls like all the other modern web service platforms. However, PHP is still PHP, so even though creating the client is now easy, figuring out what to pass is not. Here’s the code to create the client object from a wsdl (if your wsdl URL starts with HTTPS, see below):

<?php
$client = new SoapClient("http://www.thomas-bayer.com/names-service/soap?wsdl");
&#91;/sourcecode&#93;

Note the first line.  I will be omitting it from all the rest of the code samples.

Here we are working with a web method that in c# took just a plain, old string as the parameter.  I could invoke it in c# with: 

client.getNameInfo("John");

By analogy, I expected to do something similar in PHP:

&#91;sourcecode language='php'&#93;
$client = new SoapClient("http://www.thomas-bayer.com/names-service/soap?wsdl");
print $client->getNameInfo("John");

Imagine my dismay when I received this error message:

Fatal error: Uncaught SoapFault exception: [ns2:Server] java.lang.NullPointerException

I’m not even using Java here! What’s going on? This service must be implemented using Java. More importantly, you need some way to see what the problems really are and what messages were sent and/or received. Here’s where reading this post really pays off:

<?php

$client = new SoapClient(
	"http://www.thomas-bayer.com/names-service/soap?wsdl"
	,array("trace" => 1, "exceptions" => 0)
);

$functions = $client->__getFunctions();
print_r($functions);
$types = $client->__getTypes();
print_r($types);

$response = $client->getNameInfo("John");
print "<br><br>";
print_r($response);

print "<br><br><pre>\n";
	  print "Request :\n".htmlspecialchars($client->__getLastRequest()) ."\n";
	  print "Response:\n".htmlspecialchars($client->__getLastResponse())."\n";
	  print "</pre>";

Note the trace and exceptions options in line 4. In combination with lines 16-19, they allow you to see what XML was actually sent and received even if you encounter errors or Soap faults. This is very helpful in debugging. We also want to see how PHP interpreted the WSDL which is what lines 7 – 10 display. Without this information, it’s very difficult to construct the object that you will pass to the web method.

Here’s the output of the program above:

Array ( [0] => getCountriesResponse getCountries(getCountries $parameters) 
[1] => getNamesInCountryResponse getNamesInCountry(getNamesInCountry $parameters) 
[2] => getNameInfoResponse getNameInfo(getNameInfo $parameters) ) 
Array ( [0] => struct getNameInfo { string name; } 
[1] => struct getNameInfoResponse { nameInfo nameinfo; } 
[2] => struct nameInfo { string name; string gender; boolean male; boolean female; countries countries; } 
[3] => struct countries { string country; } 
[4] => struct getNamesInCountry { string country; } 
[5] => struct getNamesInCountryResponse { string name; } 
[6] => struct getCountries { } 
[7] => struct getCountriesResponse { string country; } ) 

Request :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://namesservice.thomas_bayer.com/"><SOAP-ENV:Body><ns1:getNameInfo/></SOAP-ENV:Body></SOAP-ENV:Envelope>

Response:
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:Fault xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="http://www.w3.org/2003/05/soap-envelope"><faultcode>ns2:Server</faultcode><faultstring>java.lang.NullPointerException</faultstring></ns2:Fault></S:Body></S:Envelope>

If you look at the request, you’ll see we are sending an empty getNameInfo element and the web service assumes that we sent a string. Hence, the null reference exception. Looking at line 5 above, we can see that to fix this, we need to send an object with a string member so we change:

$response = $client->getNameInfo("John");

To:

$params->name = "John";
$response = $client->getNameInfo($params);

Now our call succeeds. This publicly visible web service had a simple signature. Suppose you were working (as I was) with something more complex. In my case, I had two complex parameters being passed, the second of which included an array of a complex type. Here’s what my call ended up looking like:

$user->GroupId = "TestGroup";
$user->Username = "TestUser";

$req->CompanyID = "12345";
$req->Amount = 155.20;
$req->UserDefinedFields[0]->FieldName = "Name";
$req->UserDefinedFields[0]->FieldValue = "Jane Smith";
$req->UserDefinedFields[1]->FieldName = "TrackingCode";
$req->UserDefinedFields[1]->FieldValue = "999";

$params->user = $user;
$params->purchaseReq = $req;

$response = $client->SubmitPurchaseReq($params);

If you need access URLs using SSL, then you will need to download, deploy and enable php_openssl.dll (see steps above for php_soap.dll). In addition, you will need to copy libeay32.dll to your windows\system32 directory. Libeay32.dll is in the same zip but the parent directory of the one that php_openssl.dll is in.

Hopefully, your first PHP web services experience will go more smoothly than mine.

Complex Type Missing Members In VS2005-Generated WSDL

I was working on a web service method today in Visual Studio 2005 (.net 2.0). The return type contained an array of another type, let say X. I was using the lazy let-.net-create-the-wsld-for-me method. When I looked at the generated WSDL, the type X had no members and the sample XML that the auto-generated web page showed just had <X />. There were actually 10 or so memebers. I had flagged the class serializable. Turns out, if you don’t implement a property set, the property gets silently ignored for serialization. I guess that makes sense??