Service interfaces using XSD (cont’d MVC Web Service part 1)
The other day I got a question from a colleague of mine, regarding the XML schema usage when defining a web service interface, like the one defined in series. The question was something like this: “How will the consumer of the web service react if the, from the XSD file, auto-generated class used as in and return parameters is updated?” I couldn’t give a straight answer, other than that I assumed it would be transparent to the consumer as long as no nodes were changed or removed and only new nodes were added to the schema. I decided to give it a try, using a really simple XSD and a couple of really simple web methods. I will give a schematic picture of the scenario that I will test out later on.
Scenario
The following scenario will be tested, using a Windows Forms application as consumer and a couple of ASP.NET Web Service applications to host the web methods: 
I will implement two web services, using two different versions of the same XSD to generate the class to be used as web method parameters. The XSDs to be used will look like this, version 1 first followed by version 1.1:

As you can see, version 1.1 has an extra City node. To generate the classes use the xsd tool. Look at the syntax in . The scenario picture above shows what I am about to implement together with the following:
- The consumer will contain a web/service reference to Svc1. The endpoint address will point out Svc1 and the consumer will invoke web methods with an in parameter and return value of the type generated from XSD version 1.
- The same web/service reference as in 1 will be used but the endpoint address will point out Svc1.1. The same methods will be invoked using this endpoint.
- The consumer will contain a web/service reference to Svc1.1. The endpoint address will point out Svc1.1 and the consumer will invoke method with an in parameter and return value of the type generated from XSD version 1.1.
- The same web/service reference as in 3 will be used but the endpoint address will point out Svc1. The same methods will be invoked using this endpoint.
Implementing and testing
As always I start by adding an empty Visual Studio solution. I add two ASP.NET Web Service applications, one for XSD version 1 and one for version 1.1. After that I add a Windows Forms application to funtion as a consumer of the web services. To make use of the classes generated by the xsd tool I add the generated files using “Add existing item…”. The two web services will contain the following methods:
[WebMethod]
public XSDScenario.InOutType GetData()
{
XSDScenario.InOutType retVal = new XSDScenario.InOutType()
{
Name = "Kalle (v1)",
PersonalID = 111
};
return retVal;
}
[WebMethod]
public bool VerifyData(XSDScenario.InOutType input)
{
if (input != null)
{
if (input.Name.Length > 0 && input.PersonalID > 0)
return true;
else
return false;
}
else
{
return false;
}
}
[WebMethod]
public XSDScenario.InOutType GetData()
{
XSDScenario.InOutType retVal = new XSDScenario.InOutType()
{
Name = "Kalle (v1.1)",
City = "Stockholm",
PersonalID = 111
};
return retVal;
}
[WebMethod]
public bool VerifyData(XSDScenario.InOutType input)
{
if (input != null)
{
if (input.Name.Length > 0 && input.PersonalID > 0)
{
if (input.City != null)
return input.City.Length > 0;
else
return true;
}
else
return false;
}
else
{
return false;
}
}
In version 1.1 the method VerifyData contains an extra if statement making the method return true even if the City property of the input parameter is null. The form on the consumer side will look like this:

The form above makes it possible to test all four cases stated above. The result will be partly presented below. Before this let’s take a look at some of the web method consumer code:
private void btnInvokeGetData_Click(object sender, EventArgs e)
{
if (rbServiceVersion1.Checked)
{
// Using service reference proxy for Service version 1
SvcRef_1.InOutServiceSoapClient ioSC = new SvcRef_1.InOutServiceSoapClient();
if (rbEndpointVersion1.Checked)
{
// Using Service version 1 endpoint,
// e.g. invoking methods on version 1 Service
ioSC.Endpoint.Address =
new EndpointAddress("http://localhost:49801/InOutService.asmx");
}
else
{
// Using Service version 1.1 endpoint,
// e.g. invoking methods on version 1.1 Service
ioSC.Endpoint.Address =
new EndpointAddress("http://localhost:49802/InOutService.asmx");
}
// Since we use service reference proxy for version 1
// the GetData method will return version 1 object.
SvcRef_1.InOutType ioRet = ioSC.GetData();
}
else
{
// Using service reference proxy for Service version 1.1
SvcRef_11.InOutServiceSoapClient ioSC = new SvcRef_11.InOutServiceSoapClient();
if (rbEndpointVersion1.Checked)
{
// Using Service version 1 endpoint,
// e.g. invoking methods on version 1 Service
ioSC.Endpoint.Address =
new EndpointAddress("http://localhost:49801/InOutService.asmx");
}
else
{
// Using Service version 1.1 endpoint,
// e.g. invoking methods on version 1.1 Service
ioSC.Endpoint.Address =
new EndpointAddress("http://localhost:49802/InOutService.asmx");
}
// Since we use service reference proxy for version 1.1
// the GetData method will return version 1.1 object.
SvcRef_11.InOutType ioRet = ioSC.GetData();
}
}
private void btnInvokeVerifyData_Click(object sender, EventArgs e)
{
if (rbServiceVersion1.Checked)
{
SvcRef_1.InOutType parameter = new SvcRef_1.InOutType()
{
Name = textBoxName.Text,
PersonalID = Convert.ToInt32(textBoxPersonalID.Text)
};
// Using service reference proxy for Service version 1
SvcRef_1.InOutServiceSoapClient ioSC = new SvcRef_1.InOutServiceSoapClient();
if (rbEndpointVersion1.Checked)
{
// Using Service version 1 endpoint,
// e.g. invoking methods on version 1 Service
ioSC.Endpoint.Address =
new EndpointAddress("http://localhost:49801/InOutService.asmx");
}
else
{
// Using Service version 1.1 endpoint,
// e.g. invoking methods on version 1.1 Service
ioSC.Endpoint.Address =
new EndpointAddress("http://localhost:49802/InOutService.asmx");
}
bool dataValid = ioSC.VerifyData(parameter);
}
else
{
SvcRef_11.InOutType parameter = new SvcRef_11.InOutType()
{
City = textBoxCity.Text,
Name = textBoxName.Text,
PersonalID = Convert.ToInt32(textBoxPersonalID.Text)
};
// Using service reference proxy for Service version 1.1
SvcRef_11.InOutServiceSoapClient ioSC = new SvcRef_11.InOutServiceSoapClient();
if (rbEndpointVersion1.Checked)
{
// Using Service version 1 endpoint,
// e.g. invoking methods on version 1 Service
ioSC.Endpoint.Address =
new EndpointAddress("http://localhost:49801/InOutService.asmx");
}
else
{
// Using Service version 1.1 endpoint,
// e.g. invoking methods on version 1.1 Service
ioSC.Endpoint.Address =
new EndpointAddress("http://localhost:49802/InOutService.asmx");
}
bool dataValid = ioSC.VerifyData(parameter);
}
}
Result
I will present the results using the following pictures:
The top row shows the return parameter when consuming the GetData web method. The bottom row shows the server side parameter when invoking the VerifyData method.
As above, the top row shows the return parameter when consuming the GetData web method. The bottom row shows the server side parameter when invoking the VerifyData method.
Summary
Under these circumstances my answer was correct, when I said that the web services and consumers will still function as long as nothing is changed or removed but only new nodes are added to the XSD, meaning that the parameter objects will be extended with new fields. It is pretty hard to explain the result, but it is easy to implement the scenario defined here. Please try to do it yourself and be amused by the result! Good Luck!
This is quite a up-to-date info. I’ll share it on Digg.
How to Get Six Pack Fast
15 Apr 09 at 15:51