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.
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);
}