Want to show your appreciation? Please to my charity.

Tuesday, August 26, 2008

Integrate Web Service Client Generation into Build Process

In my last post, I presented a source code generator. It creates Web Service Client source code that implements our own domain interface. In this post, I'm going to talk about how we can integrate the generator into VS.Net.

Important Update: please also read ClickOnce Deployment + SGen Problem and The Workaround if the application is deployed through ClickOnce.

The interface between human and the generator

First of all I need a way to tell the generator what clients to generate. One idea from my colleague was to use the configuration file of Spring.Net. This is a great idea if we can get it to work but here are two major problems here.

  • It's not trivial to do this when property placeholder, abstract definition, sub-context and etc come into picture.
  • This strongly couples our generator to Spring.Net. I believe the generator is useful with or without Spring.Net.

So what I need is some sort of configuration file that let me specify the clients I want to generate. My generator must be able to easily read the configuration. Being both human and machine readable, XML is naturally the choice.

Below is an example of such an XML file (You can download all source code in this blog from http://www.codeplex.com/WSCodeGen).

<?xml version="1.0" encoding="utf-8" ?>
<web-service xmlns="urn:web-service-client-configuration-1.0">
  <client-group namespace="Example.Client.WebService" 
                xml-namespace="http://tempuri.org/">
    <client class-name="MyTestClient" xml-namespace="http://tempuri.org/">
      <interface>Example.Model.IComplex, Example.Model</interface>
      <interface>Example.Model.ISimple, Example.Model</interface>
    </client>
    <client class-name="HelloWorldClient">
      <interface>Example.Model.IHelloWorld, Example.Model</interface>
    </client>
  </client-group>
</web-service>

And yes, we have schema for it.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns="urn:web-service-client-configuration-1.0"
           targetNamespace="urn:web-service-client-configuration-1.0"
           elementFormDefault="qualified" attributeFormDefault="unqualified">
  <xs:element name="web-service">
    <xs:complexType>
      <xs:sequence maxOccurs="unbounded">
        <xs:element name="client-group">
          <xs:complexType>
            <xs:sequence maxOccurs="unbounded">
              <xs:element name="client">
                <xs:complexType>
                  <xs:sequence maxOccurs="unbounded">
                    <xs:element name="interface" type="xs:string"
                                maxOccurs="unbounded"/>
                  </xs:sequence>
                  <xs:attribute name="class-name" type="xs:Name" use="required"/>
                  <xs:attribute name="xml-namespace" type="xs:anyURI"/>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="namespace" type="xs:Name" use="required"/>
            <xs:attribute name="xml-namespace" type="xs:anyURI"
                          default="http://tempuri.org/"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Now our generator has grown from a single class to a console application project that takes an XML file and generates the Web Service client source code. With this enhancement, we can start to integrate with VS.Net.

VS.Net integration

I wish I could implement a VS.Net plug-in and/or template that it generates a Xyz.Designer.cs file based on an Xyz.xml (or may be a fancier one, Xyz.xwsc) file. Paulo Reichert's has a nice blog on this. I think I can implement what he described relatively quick. But the manual installation process of the plug-in kept me away from this solution. We have developers come and go and I don't want to be the one to answer questions like why this is not working for me. I guess I'll leave it aside until somebody is kind enough to tell me how to create an installer shell for it.

An easier, and actually also flexible approach is to make use of the VS.Net's build events. We can call the generator to generate the code in one of those build events to create the source file and then continue the build process. This worked pretty well for me and it also give a way to integrate with Spring.Net's XML configuration file. The generator command line takes an XSLT file that can be used to transform the XML configuration file to our Web Service client definition file. I'll cover the command line option in later section. Let's look at how it is setup in the example solution that you can download from http://www.codeplex.com/WSCodeGen. (Update 9/4: The example solution is updated with an additional project to workaround the ClickOnce Deployment problem)

The example solution consists of three projects illustrated in the following pictures. I think this is a simplified solution setup for most of the real world applications. If the application has only one project, I believe it is small enough to just write the clients manually.

 ExampleModel  ExampleClient  ExampleServer

We have the domain objects and interfaces defined in the Example.Model project. Both Example.Client and Example.WebService reference to Example.Model.

At the server side, you can find the HelloWorld Web Service that implements IHelloWord interface, and MyTestSerivce that implements both IComplex and ISimple interfaces.

At the client side, we created the WebServiceClient.xml file and a dummy WebServiceClient.cs file to start with. Creating the dummy file and have it included in the project is important so that the Example.Client will compile the generated clients. The XML is exactly was what I posted above.

Here comes the work horse. In the Build Events tab of the Example.Model project properties. We added command line below (in one line) to the post-build event.

$(SolutionDir)build\WebServiceClientGenerator\Debug\WebServiceClientGenerator.exe $(SolutionDir)example\Example.Client\WebService\WebServiceClient.xml

PostBuild

Rebuild the application and we should see that the WebServiceClient.cs file now contains the generated source code. Add code below to Program.cs and enable XmlSerializer diagnose in App.config.

static void Main(string[] args)
{
    IHelloWorld helloWorld = GetHelloWorldClient();
    Console.WriteLine(helloWorld.SayHello("WSCodeGen"));
    Console.ReadLine();
}
 
static IHelloWorld GetHelloWorldClient()
{
    HelloWorldClient helloWorld = new HelloWorldClient();
    helloWorld.Url = "http://localhost:3586/HelloWorld.asmx";
    return helloWorld;
}

Let's start the Example.WebService followed by the Example.Client. We can see that the temporary assembly along with other temporary files are created by XmlSerializer in your %TEMP% folder. This is because we haven't tell VS.Net to pre-compile the XmlSerializer.

The last step is to open the Build tab of the Example.Client project's properties. Change the "Configuration" to "All Configurations" and then change the "Generate serialization assembly" option to "On". Delete those generated files and run Example.Client again. In the mean time, monitor the %TEMP% folder, we should see no files are generated. Great!

ClientPropertySetting

Command line parameters

For the folks want to go beyond what was demonstrated in the Example solution, there are the command line parameters that you can use with the generator. Run the generator without any parameter will give you the usage help below.

Usage: WebServiceClientGenerator DefinitionFile [SourceFile] [XslFile]
    DefinitionFile:
        The XML definition file describes what Web Service clients to generate.
        If the XslFile is provide, the definition  file  is  transformed  using
        XSLT.
    SourceFile:
        Specifies the full path of the source file to be generated. If the file
        extension is not given, the default  source  file  extension  is  used.
        If this parameter is omitted,  default  to  the  definition  file  with
        extension replaced by the default source file extension.
    XslFile:
        If present, the definition file is transformed using this XSLT file.

I haven't gotten a chance to test it but theoretically, you should be able to pass a Spring.Net XML configuration file as the "DefinitionFile" and provide an XSL file to create the final definition file.

Future enhancement

I have started this as an open source project at http://www.codeplex.com/WSCodeGen. More API documentation is needed and Unit Test is still missing. If anybody is willing to help, thank you and please add to the comments.

Welcome suggestions and ideas, or just encouragements of creating a plug-in and template. I'll consider if there is enough demand.

I realized that we can do the same at the server side as well. It will make the next two enhancements possible if we have control at the server side.

I would like to be able to have the client throw the real exception instead of the meaningless SOAP exception that gets thrown universally by .Net framework. This is another architecture problem I would like to resolve.

I can also integrate the ability of using IList<T> and IDictionary<K,V> in web methods, we already have a solution for this using Spring.Net AOP with our home grown carrier classes. But I believe it would be much cleaner if we can do this here.

Wednesday, August 20, 2008

Generate Fast Startup .Net Web Service Client That Implements Domain Interface

In my previous blog, I provided a manual solution to the slow startup problem of Spring.Net dynamic proxy Web Service client. And I promised that I'll continue my venture to provide a better solution for applications with a few dozens of Web Service clients.

 

In this blog, I'm going to show how to use CodeDom to generate the very same source code that I have posted in the manual solution.

 

The WebServiceClientGenerator is the class that will generate the source code of a full functional Web Service client that implements a domain interface. So that the class can be used to directly replace the WebServiceProxyFactory in the application context.

 

With the help of the generator class, the same MyTestClient.cs can be generate by the code below:

 

            new WebServiceClientGenerator(

                "Example.Client", "MyTestClient",

                typeof(IMyTest), "http://tempuri.org/"

            ).Generate("..\\..");

 

The next step is some how to integrate this to the VS 2005 or NAnt build process. I need some idea...

 

Here is the generator source code. You can find the CodeUtil class in this blog.

 

/// <summary>

/// Generate source code of a Web Service client that implements a given

/// interface.

/// </summary>

public class WebServiceClientGenerator

{

    #region Private Fields

 

    private static readonly CodeTypeReference _typeSoapHttpClientProtocol =

        new CodeTypeReference(typeof(SoapHttpClientProtocol));

 

    private static readonly CodeTypeReference _typeWebServiceBindingAttribute =

        new CodeTypeReference(typeof(WebServiceBindingAttribute));

 

    private static readonly CodeTypeReference _objectArrayType =

        new CodeTypeReference(typeof(object[]));

 

    private static readonly CodeAttributeDeclaration _soapDocumentMethodAttribute =

        new CodeAttributeDeclaration(

            new CodeTypeReference(typeof(SoapDocumentMethodAttribute)));

 

    private string _fileName;

    private string _fileExtension;

    private string _classNamespace;

    private string _className;

    private CodeDomProvider _codeDomProvider;

    private CodeGeneratorOptions _codeGeneratorOptions;

    private string _wsdlNamespace;

    private Type _interfaceType;

 

    #endregion

 

    #region Public Properites

 

    /// <summary>

    /// Gets and sets the namespace for the generated class.

    /// </summary>

    public string ClassNamespace

    {

        get

        {

            return _classNamespace;

        }

        set

        {

            CheckNullSetter(value);

            _classNamespace = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the name of the generated class.

    /// </summary>

    public string ClassName

    {

        get

        {

            return _className;

        }

        set

        {

            CheckNullSetter(value);

            _className = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the file name without extension. Default to be same

    /// as <see cref="ClassName"/>.

    /// </summary>

    public string FileName

    {

        get { return _fileName ?? _className; }

        set

        {

            _fileName = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the file extension.

    /// </summary>

    public string FileExtension

    {

        get

        {

            return _fileExtension ?? CodeDomProvider.FileExtension;

        }

        set { _fileExtension = value; }

    }

 

    /// <summary>

    /// Gets and sets the interface that the generated class will implement.

    /// </summary>

    public Type Interface

    {

        get

        {

            return _interfaceType;

        }

        set

        {

            CheckNullSetter(value);

            _interfaceType = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the namespace of the Web Service.

    /// </summary>

    public string WsdlNamespace

    {

        get

        {

            return _wsdlNamespace;

        }

        set

        {

            CheckNullSetter(value);

            _wsdlNamespace = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the <see cref="CodeDomProvider"/>.

    /// Default is <see cref="CSharpCodeProvider"/>.

    /// </summary>

    public CodeDomProvider CodeDomProvider

    {

        get

        {

            if (_codeDomProvider == null)

            {

                _codeDomProvider = new CSharpCodeProvider();

            }

            return _codeDomProvider;

        }

        set

        {

            CheckNullSetter(value);

            _codeDomProvider = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the <see cref="CodeGeneratorOptions"/>.

    /// </summary>

    public CodeGeneratorOptions CodeGeneratorOptions

    {

        get

        {

            if (_codeGeneratorOptions == null)

            {

                _codeGeneratorOptions = new CodeGeneratorOptions();

            }

            return _codeGeneratorOptions;

        }

        set

        {

            CheckNullSetter(value);

            _codeGeneratorOptions = value;

        }

    }

 

    #endregion

 

    #region Constructors

    /// <summary>

    /// Construct a new generator instance.

    /// </summary>

    /// <param name="classNamespace">

    /// The namespace for the class being generated.

    /// </param>

    /// <param name="className">

    /// The name of the class.

    /// </param>

    /// <param name="interfaceType">

    /// The interface to implement.

    /// </param>

    /// <param name="wsdlNamespace">

    /// The namespace of the Web Service.

    /// </param>

    /// <exception cref="ArgumentNullException">

    /// When any parameter is <see langword="null"/>.

    /// </exception>

    public WebServiceClientGenerator(

        string classNamespace, string className,

        Type interfaceType, string wsdlNamespace)

    {

        CheckArgumentNull(classNamespace, "classNamespace");

        CheckArgumentNull(className, "className");

        CheckArgumentNull(interfaceType, "interfaceType");

        CheckArgumentNull(wsdlNamespace, "wsdlNamespace");

 

        _classNamespace = classNamespace;

        _className = className;

        _interfaceType = interfaceType;

        _wsdlNamespace = wsdlNamespace;

    }

    #endregion

 

    #region Public Instance Methods

 

    /// <summary>

    /// Generate the source file in a directory specified by

    /// <paramref name="filePath"/>.

    /// </summary>

    /// <remarks>

    /// The name of the file is determined by the <see cref="FileName"/>

    /// property and the <see cref="FileExtension"/> property.

    /// </remarks>

    /// <param name="filePath">

    /// The directory where the source file will be generated.

    /// </param>

    /// <exception cref="ArgumentNullException">

    /// When <paramref name="filePath"/> is <see langword="null"/>.

    /// </exception>

    public void Generate(string filePath)

    {

        CheckArgumentNull(filePath, "filePath");

        string fullPath = Path.Combine(filePath, FileName + "." + FileExtension);

        using (Stream s = File.Open(fullPath, FileMode.Create))

        {

            StreamWriter sw = new StreamWriter(s);

            Generate(sw);

            //Close StreamWriter

            sw.Close();

            s.Close();

        }

    }

 

    /// <summary>

    /// Write out the code to the <paramref name="writer"/>.

    /// </summary>

    /// <param name="writer">

    /// The writer to where the generated code will be sent.

    /// </param>

    /// <exception cref="ArgumentNullException">

    /// When <paramref name="writer"/> is <see langword="null"/>.

    /// </exception>

    public void Generate(TextWriter writer)

    {

        CheckArgumentNull(writer, "writer");

 

        ICodeGenerator generator = CodeDomProvider.CreateGenerator(writer);

        CodeGeneratorOptions options = CodeGeneratorOptions;

 

        //Disable documentation warning

        generator.GenerateCodeFromCompileUnit(

            new CodeSnippetCompileUnit("#pragma warning disable 1591"),

            writer, options);

 

        // generate the code

        generator.GenerateCodeFromNamespace(

            ImplementWebServiceClient(ClassNamespace, ClassName, Interface, WsdlNamespace),

            writer, options);

 

        //Restore documentation warning

        generator.GenerateCodeFromCompileUnit(

            new CodeSnippetCompileUnit("#pragma warning restore 1591"),

            writer, options);

    }

 

    #endregion

 

    #region Private Methods

 

    // implements the web service client

    private static CodeNamespace ImplementWebServiceClient(

        string classNamespace,

        string className,

        Type interfaceType,

        string wsdlNamespace)

    {

        //Create Namespaces

        CodeNamespace codeNamespace = new CodeNamespace(classNamespace);

 

        //Create web service binding attribute

        CodeAttributeDeclaration webServiceBindingAttribute =

            new CodeAttributeDeclaration(

                _typeWebServiceBindingAttribute,

                new CodeAttributeArgument(

                    "Namespace",

                    new CodePrimitiveExpression(wsdlNamespace)

                    )

                );

 

        //Create Class Declaration

        CodeTypeDeclaration codeClass = CodeUtils.DefineClass(

            new CodeAttributeDeclaration[] { webServiceBindingAttribute },

            TypeAttributes.Public,

            className,

            _typeSoapHttpClientProtocol,

            new CodeTypeReference(interfaceType));

 

        // implements interface

        codeClass.Members.AddRange(ImplementInterface(interfaceType));

 

        codeNamespace.Types.Add(codeClass);

 

        return codeNamespace;

    }

 

    // implements all the methods defined in the interface.

    private static CodeMemberMethod[] ImplementInterface(Type theInterface)

    {

        MethodInfo[] methods = theInterface.GetMethods();

        CodeMemberMethod[] codeMethods = new CodeMemberMethod[methods.Length];

        for (int i = 0; i < methods.Length; i++)

        {

            codeMethods[i] = ImplementWebMethod(methods[i]);

        }

        return codeMethods;

    }

 

    // implement one web method

    private static CodeMemberMethod ImplementWebMethod(MethodInfo method)

    {

        CodeTypeReference returnType =

            new CodeTypeReference(method.ReturnType);

 

        CodeMemberMethod codeMethod = CodeUtils.DefineMethod(

            new CodeAttributeDeclaration[] { _soapDocumentMethodAttribute },

            MemberAttributes.Public,

            returnType,

            method.Name);

 

        // Define all parameters and seperate them by in/out parameter

        // type. Ref type is considered both in and out.

        List<CodeVariableReferenceExpression> inParams =

            new List<CodeVariableReferenceExpression>();

 

        List<OutParam> outParams = new List<OutParam>();

 

        foreach (ParameterInfo param in method.GetParameters())

        {

            // Determine the true parameter type

            Type paramType = param.ParameterType;

            if (paramType.IsByRef) paramType = paramType.GetElementType();

 

            CodeTypeReference codeParamType =

                new CodeTypeReference(paramType);

 

            FieldDirection direction =

                CodeUtils.DetermineParameterDirection(param);

 

            CodeVariableReferenceExpression codeParam =

                CodeUtils.DefineParameter(

                    codeMethod, codeParamType, direction, param.Name);

 

            if (direction != FieldDirection.Out) // In or Ref

            {

                inParams.Add(codeParam);

            }

 

            if (direction != FieldDirection.In) // Out or Ref

            {

                //outParamTypes.Add(codeParamType);

                outParams.Add(new OutParam(codeParamType, codeParam));

            }

 

        }

 

        // Calls the Invoke method on base class with all 'in' parameters

        CodeMethodInvokeExpression invokation = new CodeMethodInvokeExpression(

            new CodeThisReferenceExpression(), "Invoke",

            new CodePrimitiveExpression(method.Name),

            new CodeArrayCreateExpression(_objectArrayType, inParams.ToArray())

            );

 

        // Define the result variable and initialize it with the result of

        // the invoke method.

        CodeVariableReferenceExpression result = CodeUtils.DefineVariable(

            codeMethod, _objectArrayType, "result", invokation);

 

        // Return value and out parameter index

        int index = 0;

 

        // Define the return statement

        CodeMethodReturnStatement codeReturn = new CodeMethodReturnStatement();

        if (method.ReturnType != typeof(void))

        {

            // Return the result if return type is not void.

            codeReturn.Expression = new CodeCastExpression(

                returnType, CodeUtils.ArrayIndex(result, index++));

        }

 

        // Assign result to 'out' parameters

        foreach (OutParam outParam in outParams)

        {

            codeMethod.Statements.Add(

                new CodeAssignStatement(

                    outParam.Variable,

                    new CodeCastExpression(outParam.Type,

                                           CodeUtils.ArrayIndex(result, index++))

                    )

                );

        }

 

        // Add the return statement

        codeMethod.Statements.Add(codeReturn);

 

        return codeMethod;

    }

 

    private static void CheckArgumentNull(object value, string name)

    {

        if (value == null)

        {

            throw new ArgumentNullException(name);

        }

    }

 

    private static void CheckNullSetter(object value)

    {

        CheckArgumentNull(value, "value");

    }

 

    #endregion

 

    private struct OutParam

    {

        public CodeTypeReference Type;

        public CodeVariableReferenceExpression Variable;

 

        public OutParam(

            CodeTypeReference type,

            CodeVariableReferenceExpression variable)

        {

            Type = type;

            Variable = variable;

        }

    }

}

 

Generate Fast Startup .Net Web Service Client That Implements Domain Interface

Tuesday, August 19, 2008

Slow Startup Performance with Spring.Net WebServiceProxyFactory

Update: This is the beginning of a series blog posts of how this problem is described and eventually addressed. I'm including a table of content here for easier navigation.

Table Of Content

Describe the Problem: Slow Startup Performance with Spring.Net WebServiceProxyFactory (this post)

Propose the Solution: Generate Fast Startup .Net Web Service Client That Implements Domain Interface

Implement the Solution: Integrate Web Service Client Generation into Build Process

Resolve ClickOnce Deployment Issue: ClickOnce Deployment + SGen Problem and The Workaround

Integrate with Spring.Net: Using WSCodeGen with Spring.Net

Release the WSCodeGen Project: Getting Ready for WSCodeGen 1.0.1 Release

Conclusion: Web Service Client Startup Performance: Spring.Net Proxy v.s. WSCodeGen

If anybody is still writing the Web Service using the WebService and WebMethod attributes at server side and adding Web Reference at the client size, then they should seriously look at how Spring.Net does the Web Service.

Let's list some benefits of using Spring.Net for Web Service.

  1. You can just write your web method in PONO and ask Spring.Net to expose it as Web Service.
  2. Your PONO will typically implement a service interface. And of course, you can use dependency inject.
  3. Spring.Net can expose the same PONO as other type of service, for example .Net Remoting.
  4. At the client side, Spring.Net can generate a Web Service client dynamic proxy that implements the same interface.
  5. Because the proxy client implements the same interface that the service object does. You can actually use your own domain objects in the Web Service calls. Just like calling directly calling the service object at the server side. Cool, isn't it?

Yes, we have been using it in our projects and running in production for a year now. Everything was great until recently we started to look at the startup performance of our SmartClient application.

Regardless of being Web Reference or Spring.Net dynamic proxy, they all inherit from SoapHttpClientProtocol class. That class uses XmlSerializer to convert between object and XML. When the first time XmlSerializer for a given type was created, it actually creates a temporary assembly using c# compiler. You can see those temporary files in the %TEMP% folder if you configure your application to diagnose the XmlSerializer by adding the following XML snip to the app.config file. This compiling process is slow and it does this for every type it encounters.

  <!--

   Never deliver your application with this.

   It fills up your temp folder in no time.

  -->

  <system.diagnostics>

    <switches>

      <add name="XmlSerialization.Compilation" value="4"/>

    </switches>

  </system.diagnostics>

When our SmartClient needs to connect to multiple Web Services at startup, the XmlSerializer assembly creation process was taking significant amount of time during application startup.

Microsoft realized that problem so a utility called SGen.exe was released. SGen scans the types in an assembly, say Example.dll, and generates all serializer into a sister assembly, named Example.XmlSerializers.dll. VS 2005 also provides "generate serialization assembly" on the Build tab of project property. Grant Drake has a nice blog about this.

When the "generate serialization assembly" option was set to Auto or On, the SGen does generate the serializer assembly for Web Reference client to eliminate the temporary assembly creation in runtime. This greatly improves the runtime startup performance.

But when come to the Spring.Net dynamic proxy Web Service client, no mater how I tweaked the options, the temporary assembly was always created in runtime which seriously impacts the startup performance of the client applications, especially when multiple dynamic proxies are used on a slower PC.

I started a thread about this problem at the Spring.Net's support forum back in Feb 2008. Since then, I started to explore a way out myself.

My first venture was to manually create the Web Service client. I copied the code from the Web Reference generated source and changed the class to implements our service interface. I can then use this class to replace the dynamic proxy in the Spring.Net's application context. SGen is happily pre-compiling the serializer for it, just like it does to Web Reference client!

Look at the following code as an example of Web Service clients that I have manually created. This solution works, fast, not difficult to write. It is a good option for a client that only calls a few Web Services. But for a client that talks to a few dozens of Web Services, I have to say that it is repeated task and tedious. Can we generate them in source code at compile time instead of using dynamic proxy at runtime? stay tuned...

#pragma warning disable 1591

namespace Example.Client {

 

 

    [System.Web.Services.WebServiceBindingAttribute(Namespace="http://tempuri.org/")]

    public class MyTestClient : System.Web.Services.Protocols.SoapHttpClientProtocol, Example.BizService.Client.Program.IMyTest {

 

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute()]

        public virtual void Simple() {

            object[] result = this.Invoke("Simple", new object[0]);

            return;

        }

 

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute()]

        public virtual void OneParam(int i) {

            object[] result = this.Invoke("OneParam", new object[] {

                        i});

            return;

        }

 

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute()]

        public virtual void AnotherParam(Example.BizService.Client.Program.Another i) {

            object[] result = this.Invoke("AnotherParam", new object[] {

                        i});

            return;

        }

 

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute()]

        public virtual int SimpleReturn() {

            object[] result = this.Invoke("SimpleReturn", new object[0]);

            return ((int)(result[0]));

        }

 

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute()]

        public virtual int OneParamReturn() {

            object[] result = this.Invoke("OneParamReturn", new object[0]);

            return ((int)(result[0]));

        }

 

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute()]

        public virtual string RefParam(ref int i, int j) {

            object[] result = this.Invoke("RefParam", new object[] {

                        i,

                        j});

            i = ((int)(result[1]));

            return ((string)(result[0]));

        }

 

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute()]

        public virtual string OutParam(out int i) {

            object[] result = this.Invoke("OutParam", new object[0]);

            i = ((int)(result[1]));

            return ((string)(result[0]));

        }

 

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute()]

        public virtual string RefAnother(ref Example.BizService.Client.Program.Another a, out int i) {

            object[] result = this.Invoke("RefAnother", new object[] {

                        a});

            a = ((Example.BizService.Client.Program.Another)(result[1]));

            i = ((int)(result[2]));

            return ((string)(result[0]));

        }

 

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute()]

        public virtual void OutAnother(out Example.BizService.Client.Program.Another a) {

            object[] result = this.Invoke("OutAnother", new object[0]);

            a = ((Example.BizService.Client.Program.Another)(result[0]));

            return;

        }

    }

}

#pragma warning restore 1591