ASMX and Optional Arguments

Today I’ve got a task to modify an existing ASXM WebService method by adding one more boolean parameter to it. The parameter should be optional (and if its value is not set by user it should be set to ‘True’).

[WebMethod(Description = "Returns all main categories", MessageName = "GetCategories")]
public List<PublicWSCategoryItem> GetCategories(string culture)
{
    return GetCategories(culture, includeHiddenCategories: true);
}

[WebMethod(Description = "Returns all main categories", MessageName = "GetCategoriesByVisibility")]
public List<PublicWSCategoryItem> GetCategoriesByVisibility(string culture, bool? includeHiddenCategories)
{
    return GetCategories(culture, includeHiddenCategories ?? true);
}

private List<PublicWSCategoryItem> GetCategories(string culture, bool includeHiddenCategories)
{
    // data processing

The web service is consumed by many customers so I had to keep in mind that my modification after releasing should work for all customers (for those who will update web service reference and for  those who won’t do it). BTW, we’re using .NET 4.0.

 

First idea – optional argument

[WebMethod(Description = "Returns all main categories")]
public List<PublicWSCategoryItem> GetCategories(string culture, bool includeHiddenCategories = true)
{
    // …

Let’s check how the generated WSDL looks like:

image

The includeHiddenCategories parameter seems to be required, and this is not how it is meant to be 😦 But to be sure of it I have to call the Web Service method. I opened Storm, added reference to Web Service, slightly modified SOAP message, and then I called the method.

image

I checked the value of the includeHiddenCategories parameter that was passed to the WS method.

image

Unfortunately, the value of the includeHiddenCategories parameter that is ‘seen’ inside the method is ‘False’ instead of required ‘True’. Shit!

 

Second idea – let’s change the type of the parameter from bool to Nullable<bool>

[WebMethod(Description = "Returns all main categories")]
public List<PublicWSCategoryItem> GetCategories(string culture, bool? includeHiddenCategories = true)
{
    // …

The corresponding generated service description:

image

The parameter seems to be required (minOccurs=”1”, maxOccurs=”1”), however still it can be nillable. It is a bit misleading. According to the W3C Recommendation, “an element is required to appear when the value of minOccurs is 1 or more”.

Again, I checked the value of includeHiddenCategories parameter that was passed to the WS method.

image

Unfortunately, the value of the includeHiddenCategories parameter that is ‘seen’ inside the method is ‘null’, instead of required ‘True’. And again, it is not how it is supposed to be. Even worse, right now the parameter can have three possible values (null, ‘True’, ‘False’), and this is completely unacceptable from the business point of view.

Being a bit confused, I’ve decided to stop working on optional argument and to start completely new approach.

 

Third idea – overloaded method

The idea is quite simple, two methods with the same name that differ from each other in terms of number of input parameters. One method accepting only one parameter (string) and the second method accepting two parameters (string, bool?). We can also create the third private method accepting two parameter of type string and bool, that will be responsible for the main logic. As I said, that seems to be easy to implement, so let’s do it.

[WebMethod(Description = "Returns all main categories")]
public List<PublicWSCategoryItem> GetCategories(string culture)
{
    return GetCategories(culture, includeHiddenCategories: true);
}

[WebMethod(Description = "Returns all main categories")]
public List<PublicWSCategoryItem> GetCategories(string culture, bool? includeHiddenCategories)
{
    return GetCategories(culture, includeHiddenCategories ?? true);
}

private List<PublicWSCategoryItem> GetCategories(string culture, bool includeHiddenCategories)
{
    // data processing

Being almost happy I’ve started testing my implementation. F5 and I checked the web browser…

image

Crap! As the message says, the ASMX web services do not allow method overloading as it can be done in the general C# code! Ok, the exception message suggests to use the MessageName properties, and each message name should be unique. That seems to be easy to fix it. Task almost completed???

Very quickly I’ve added the MessageName attributes for both WebMethods attributes:

[WebMethod(Description = "Returns all main categories", MessageName = "GetCategories")]
public List<PublicWSCategoryItem> GetCategories(string culture)
{
    return GetCategories(culture, includeHiddenCategories: true);
}

[WebMethod(Description = "Returns all main categories", MessageName = "GetCategoriesByVisibility")]
public List<PublicWSCategoryItem> GetCategories(string culture, bool? includeHiddenCategories)
{
    return GetCategories(culture, includeHiddenCategories ?? true);
}

private List<PublicWSCategoryItem> GetCategories(string culture, bool includeHiddenCategories)
{
    // data processing

Again, I pressed F5 and checked the web browser…

image

Crap! There’s a problem again! Although the previous error message suggested to set unique MessageName properties and I did it, I have still problems! Wrrrrr….

 

Fourth idea – two methods with different names and with different MessageName properties

Finally I have decided to skip the idea of method overloading and to use 2 different method names instead.

[WebMethod(Description = "Returns all main categories", MessageName = "GetCategories")]
public List<PublicWSCategoryItem> GetCategories(string culture)
{
    return GetCategories(culture, includeHiddenCategories: true);
}

[WebMethod(Description = "Returns all main categories", MessageName = "GetCategoriesByVisibility")]
public List<PublicWSCategoryItem> GetCategoriesByVisibility(string culture, bool? includeHiddenCategories)
{
    return GetCategories(culture, includeHiddenCategories ?? true);
}

private List<PublicWSCategoryItem> GetCategories(string culture, bool includeHiddenCategories)
{
    // data processing

Just to be sure that everything is fine I called the GetCategoriesByVisibility() and observed the value of the parameter being passed to the private GetCategories() methods.

The slightly changed SOAP message:

image

and the results I got while debugging:

image

Yes, yes, yes! Finally I have got what I have been supposed to do!

 

Conclusion

The optional argument, the new feature of .NET 4.0, is not supported by ASMX WebServices (or at least the generated WSDL service documentation suggests, that it is not supported). The another conclusion is that it is better not to use overloaded method names as there is no such concept in WSDL. Lesson learned.