Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class generation from XSD during runtime

Tags:

c#

How do I generate a C# class from XSD at runtime?

Additionally, how do it list the properties that are contained by the type?

like image 442
Dave Avatar asked Oct 19 '22 13:10

Dave


1 Answers

It definitely is possible.. and not too complicated. You just have to add some differen techniques. You can use the "Description Importer" to import Service descriptions in runtime. Link

What I did was basically creating following steps:

1) Get the WSDL file with a reader (locally or remote, different approaches)

XmlTextReader myXmlReader;
myWebService = new WebServiceImporterCompiler(WSDLPath, soapVersion);

if (useLocalWSDL)
{
     FileWebRequest wr = (FileWebRequest)FileWebRequest.Create(WSDLPath);
     FileWebResponse wres = (FileWebResponse)wr.GetResponse();
     myXmlReader = new XmlTextReader(wres.GetResponseStream());
}
else
{
     Uri uri = new Uri(WSDLPath); //WEBSERVICE URI
     HttpWebRequest wr = (HttpWebRequest)HttpWebRequest.Create(uri.OriginalString + "?wsdl");
     wr.Credentials = wr.Credentials = new NetworkCredential(userName, password ?? "");
     HttpWebResponse wres = (HttpWebResponse)wr.GetResponse();
     myXmlReader = new XmlTextReader(wres.GetResponseStream());
}

2) Build an assembly from the definition / myXmlReader

Check if the xml is readable

if (!System.Web.Services.Description.ServiceDescription.CanRead(myXmlReader)) 
{
           throw new IOException("WSDL not readable");
}

Load importer with some basic options (you might add / change something here) I create an assembly (dll) but with the switch parameters.GenerateInMemory you will be able to generate an in memory class.

ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
ServiceDescription serviceDescription = ServiceDescription.Read(myXmlReader);
descriptionImporter.ProtocolName = soapVersion.ToString(); // EITHER SOAP OR SOAP12
descriptionImporter.AddServiceDescription(serviceDescription, null, null);
descriptionImporter.Style = ServiceDescriptionImportStyle.Client;
descriptionImporter.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;

Compile assembly with CodeDomProvider

CodeCompileUnit codeUnit = new CodeCompileUnit();
CodeNamespace codeNamespace = new CodeNamespace();
codeUnit.Namespaces.Add(codeNamespace); // Add additional Namespaces
ServiceDescriptionImportWarnings importWarnings = descriptionImporter.Import(codeNamespace, codeUnit);
if (importWarnings == 0)
{
      using (CodeDomProvider compiler = CodeDomProvider.CreateProvider("CSharp"))
      {
           string[] references = { "System.dll", "System.Web.Services.dll", "System.Xml.dll" };
           CompilerParameters parameters = new CompilerParameters(references); 
           parameters.GenerateExecutable = false;
           parameters.GenerateInMemory = false;
           parameters.IncludeDebugInformation = false;
           parameters.CompilerOptions = "/optimize";
           parameters.TempFiles = new TempFileCollection(System.IO.Path.GetTempPath() + "xxx", false);
           parameters.ReferencedAssemblies.Add("System.dll");

           results = compiler.CompileAssemblyFromSource(parameters, CSharpCode);
           foreach (CompilerError cError in results.Errors)
           {
               // log errors
           }
           if (results.Errors.Count > 0 || results.CompiledAssembly == null) throw new Exception("Kompilierfehler bei Assemblyerstellung");
       }
 }

3) Use the generated asssembly object to invoke methods for example for calling the service

public T InvokeMethod <T>(Assembly assembly, string serviceNameToCall, MethodInfo methodToCall)
{
        SoapHttpClientProtocol mySoapProtocoll;
    try
    {
         object serviceInstance = myAssembly.CreateInstance(serviceNameToCall);
         mySoapProtocoll = (SoapHttpClientProtocol)serviceInstance;
         mySoapProtocoll.Credentials = CredentialCache.DefaultCredentials; // or use your own
         object myObject = (T)ServiceType.InvokeMember(methodToCall, BindingFlags.InvokeMethod, null, mySoapProtocoll, args);
    }
}

To get the methodInfo object / available methods use reflections to iterate over the assembly / classes.

A complete guide on reflections can be found here

like image 159
Marc Wittmann Avatar answered Nov 13 '22 06:11

Marc Wittmann