2007-11-05

Accediendo dinamicamente a WebServices

Hoy estábamos viendo con uno de los chicos del laburo que necesitaba poder acceder a distintos WebServices (desconocidos para el en tiempo de compilación) y queríamos encontrar una forma cómoda de hacerlo.

Después de buscar un poco, encontramos una solución que pareció bastante piola: usando el WSDL del WebService, generar la clase proxy en runtime y usar los WebMethods por Reflection. Está claro que no es la solución óptima en términos de velocidad, pero con unas clases helper que hice, resulta muy cómodo para usar.

El siguiente código lee el WSDL y genera la nueva clase en runtime:




try

{

    //Get the web service description

    Uri uri = new Uri(wsdlUrl);

 

    WebRequest webRequest = WebRequest.Create(uri);

    System.IO.Stream requestStream = webRequest.GetResponse().GetResponseStream();

    ServiceDescription sd = ServiceDescription.Read(requestStream);

    string sdName = sd.Services[0].Name;

 

    //Create the ServiceDescriptionImporter

    ServiceDescriptionImporter sdImporter = new ServiceDescriptionImporter();

    sdImporter.AddServiceDescription(sd, String.Empty, String.Empty);

    sdImporter.ProtocolName = "Soap";

    sdImporter.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;

 

    CodeNamespace codeNamespace = new CodeNamespace(DefaultNamespace);

    CodeCompileUnit compileUnit = new CodeCompileUnit();

    compileUnit.Namespaces.Add(codeNamespace);

 

    //Import the ServiceDescription

    ServiceDescriptionImportWarnings warnings = sdImporter.Import(codeNamespace, compileUnit);

 

    if (warnings == 0) //No warnings

    {

        CSharpCodeProvider codeProvider = new CSharpCodeProvider();

        codeProvider.GenerateCodeFromNamespace(codeNamespace, new StringWriter(), new CodeGeneratorOptions());

 

        //Compile the assembly

        string[] assemblyReferences = new string[2] { "System.Web.Services.dll", "System.Xml.dll" };

        CompilerParameters param = new CompilerParameters(assemblyReferences);

        param.GenerateExecutable = false;

        param.GenerateInMemory = true;

        param.TreatWarningsAsErrors = false;

        param.WarningLevel = 4;

 

        CompilerResults results = codeProvider.CompileAssemblyFromDom(param, compileUnit);

        Assembly assembly = results.CompiledAssembly;

 

        Type[] types = assembly.GetExportedTypes();

        //Get the generated Type

        return new DynamicProxyClass(types[0], GetMethodNames(sd.PortTypes[0].Operations));

    }

    else

    {

        throw new Exception(String.Format("Importing has warnings: {0}", warnings.ToString()));

    }

}

catch (Exception ex)

{

    throw new Exception("Couldn't generate proxy class (see inner exception)", ex);

}



Acá se puede ver que lo que devuelvo es una DynamicProxyClass. Esta clase lo que hace es wrappear la clase generada y exponer solo los métodos del WebService, sus tipos de retorno y sus parámetros.

Si les interesa jugar con esto, dejo para bajar el código completo de la solución.


Z

No hay comentarios.: