Search code examples
c#asp.netweb-servicesreflectionwsdl

Reflecting ASMX via WSDL, with custom types


I need to reflect an ASMX web service via WSDL. Simple cases go all fine, using the accepted answer of this. But then I have some complex type (a strong-typed dataset) defined in there. The original source of the service looks like

[WebMethod]
public int GetCustomer(string civicRegistrationNo, out MyCompany.E_WebServices.DataSets.CustomerDataSet customerDS)

This CustomerDataSet gives me trouble, because in the reflection the method comes as

GetCustomer [Int32]: civicRegistrationNo [String], customerDS [out XmlElement]

whereas what I would like to see (and what comes out if I reflect the DLL directly) is

GetCustomer [Int32]: civicRegistrationNo [String], customerDS [out CustomerDataSet]

How should I improve my code (below) to get the type CustomerDataSet coming correctly (not as XmlElement)? Sure it has something to do with this block in the WSDL

<s:import namespace="http://tempuri.org/CustomerDataSet.xsd"/>
<s:import schemaLocation="http://localhost/E-WebServices/WSCustomer.asmx?schema=CustomerDataSet" namespace="http://tempuri.org/CustomerDataSet.xsd"/>

and yes I can see that definition in the browser if I open

http://localhost/E-WebServices/WSCustomer.asmx?schema=CustomerDataSet

But how to get it coming from this code below?

var client = new System.Net.WebClient();
var stream = client.OpenRead("http://localhost/E-WebServices/WSCustomer.asmx?wsdl");
var description = ServiceDescription.Read(stream);
var importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap12";
importer.AddServiceDescription(description, null, null);
importer.Style = ServiceDescriptionImportStyle.Client;
importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;
var nmspace = new CodeNamespace();
var unit1 = new CodeCompileUnit();
unit1.Namespaces.Add(nmspace);
var warning = importer.Import(nmspace, unit1);
if (warning == 0)
{
    CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");
    string[] assemblyReferences = new string[5] { "System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll" };
    CompilerParameters parms = new CompilerParameters(assemblyReferences);
    CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);
    if (results.Errors.Count > 0)
    {
        foreach (CompilerError oops in results.Errors)
            Debug.WriteLine(oops.ErrorText);
        throw new Exception("Compile Error Occured calling webservice");
    }
    object service = results.CompiledAssembly.CreateInstance("WSCustomer");
    List<MethodInfo> methods = service.GetType().GetMethods().ToList();
    // use them somehow
}

I am kind of inspired by what Visual Studio does itself - if web ref is added, it only has the WSDL (it doesn't go directly to that DLL even if it's in the same machine, I think), and yet it can derive the included type nicely. So I figure it must be possible!? enter image description here


Solution

  • OK, it seems I got it working (maybe not the cleanest way)

    string serviceUrl = "http://localhost/E-WebServices/WSCustomer.asmx";
    var client = new WebClient();
    ServiceDescription descr;
    using (var stream = client.OpenRead(serviceUrl + "?wsdl"))
    {
        descr = ServiceDescription.Read(stream);
    }
    var importer = new ServiceDescriptionImporter()
    {
        ProtocolName = "Soap12",
        Style = ServiceDescriptionImportStyle.Client,
        CodeGenerationOptions = CodeGenerationOptions.GenerateProperties,
    };
    importer.AddServiceDescription(descr, null, null);
    // Add any imported schemas
    var importedSchemas = new List<string>();
    foreach (XmlSchema wsdlSchema in descr.Types.Schemas)
    {
        foreach (XmlSchemaObject externalSchema in wsdlSchema.Includes)
        {
            if (externalSchema is XmlSchemaImport)
            {
                XmlSchemaImport schemaImport = externalSchema as XmlSchemaImport;
                var split = schemaImport.Namespace.Split(new char[] { '/', '.' });
                string schemaId = split[split.Count() - 2];
                if (importedSchemas.Contains(schemaId))
                    continue;
                importedSchemas.Add(schemaId);
                Uri schemaUri = new Uri(serviceUrl + "?schema=" + schemaId);
                XmlSchema schema;
                using (var wsdlStream = client.OpenRead(schemaUri))
                {
                    schema = XmlSchema.Read(wsdlStream, null);
                }
                importer.Schemas.Add(schema);
            }
        }
    }
    var nmspace = new CodeNamespace();
    var unit1 = new CodeCompileUnit();
    unit1.Namespaces.Add(nmspace);
    var warning = importer.Import(nmspace, unit1);
    if (warning == 0)
    {
        CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");
        string[] assemblyReferences = new string[5] { "System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll" };
        CompilerParameters parms = new CompilerParameters(assemblyReferences);
        CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);
        if (results.Errors.Count > 0)
        {
            foreach (CompilerError oops in results.Errors)
                Debug.WriteLine(oops.ErrorText);
            throw new Exception("Compile Error Occured calling webservice");
        }
        object service = results.CompiledAssembly.CreateInstance("WSCustomer");
        Type t = service.GetType();
        List<MethodInfo> methods = t.GetMethods().ToList();
        // use them somehow
    }