L'intégration de services Web dans les applicaitons C# peut être réalisée de plusieurs manières. Les deux approches principales consistent à utiliser la fonctionnalité "Ajouter une référence de service" intégrée à Visual Studio ou à implémenter une solution personnalisée basée sur la réflexion et la génération dynamique de code.
Méthode 1 : Utilisation de la référence de service Visual Studio
Cette méthode est la plus directe et recommandée pour la plupart des scénarios. Elle consiste à ajouter une référence au service Web directement depuis votre projet.
- Dans l'Explorateur de solutions, faites un clic droit sur votre projet.
- Sélectionnez "Ajouter" > "Référence de service".
- Dans la fenêtre "Configurer les références de service", entrez l'URL du fichier WSDL du service Web dans le champ "Adresse".
- Cliquez sur "Accéder" pour rechercher le service.
- Une fois le service trouvé, cliquez sur "OK". Visual Studio générera le code proxy nécessaire.
L'exemple de code suivant montre comment appeler une méthode nommée interfaceTest sur un service référencé :
// Assurez-vous que le namespace correspond à celui généré par Visual Studio
// Ici, nous supposons que le namespace est ServiceReference1
ServiceReference1.ImportDataServiceClient serviceClient = new ServiceReference1.ImportDataServiceClient();
string result = serviceClient.interfaceTest("666");
MessageBox.Show(result);
Méthode 2 : Génération dynamique de proxy avec réflexion
Cette approche offre plus de flexibilité, notamment pour les scénarios où l'URL du service Web n'est pas connue au moment de la compilation ou pour gérer des services Web de manière dynamique. Elle implique la création d'une classe proxy qui télécharge le fichier WSDL, génère une classe de proxy à la volée, la compile en un DLL temporaire, puis utilise la réflexion pour invoquer les méthodes du service.
Voici une implémentation possible d'une telle classe proxy :
using System;
using System.IO;
using System.Reflection;
using System.Web.Services.Description;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Collections.Generic;
/// <summary>
/// Classe pour l'appel dynamique de services Web sans référence de service préalable.
/// </summary>
public class DynamicWebServiceProxy
{
private string wsdlUrl;
private string serviceName;
private string proxyNamespaceFormat = "DynamicProxy.{0}";
private Type proxyType;
private string assemblyName;
private string assemblyPath;
private object serviceInstance;
/// <summary>
/// Initialise une nouvelle instance de la classe DynamicWebServiceProxy.
/// </summary>
/// <param name="serviceWsdlUrl">L'URL du fichier WSDL du service Web.</param>
public DynamicWebServiceProxy(string serviceWsdlUrl)
{
this.wsdlUrl = serviceWsdlUrl;
this.serviceName = GetServiceNameFromUrl(serviceWsdlUrl);
this.assemblyName = string.Format(proxyNamespaceFormat, serviceName);
this.assemblyPath = Path.Combine(Path.GetTempPath(), assemblyName + GetMd5Hash(this.wsdlUrl) + ".dll");
GenerateAndLoadAssembly();
}
/// <summary>
/// Initialise une nouvelle instance de la classe DynamicWebServiceProxy avec un nom de service spécifié.
/// </summary>
/// <param name="serviceWsdlUrl">L'URL du fichier WSDL du service Web.</param>
/// <param name="customServiceName">Le nom personnalisé pour le service.</param>
public DynamicWebServiceProxy(string serviceWsdlUrl, string customServiceName)
{
this.wsdlUrl = serviceWsdlUrl;
this.serviceName = customServiceName;
this.assemblyName = string.Format(proxyNamespaceFormat, serviceName);
this.assemblyPath = Path.Combine(Path.GetTempPath(), assemblyName + GetMd5Hash(this.wsdlUrl) + ".dll");
GenerateAndLoadAssembly();
}
/// <summary>
/// Exécute une méthode du service Web et retourne le résultat.
/// </summary>
/// <param name="methodName">Le nom de la méthode à exécuter.</param>
/// <param name="parameters">Les paramètres à passer à la méthode.</param>
/// <returns>L'objet retourné par la méthode du service Web.</returns>
public object InvokeMethod(string methodName, object[] parameters)
{
if (this.proxyType == null)
{
throw new InvalidOperationException($"Le type proxy pour le service '{this.serviceName}' n'a pas pu être chargé.");
}
MethodInfo method = this.proxyType.GetMethod(methodName);
if (method == null)
{
throw new MissingMethodException($"La méthode '{methodName}' n'a pas été trouvée dans le service Web.");
}
try
{
if (this.serviceInstance == null)
{
this.serviceInstance = Activator.CreateInstance(this.proxyType);
}
return method.Invoke(this.serviceInstance, parameters);
}
catch (TargetParameterCountException)
{
throw new ArgumentException($"Le nombre de paramètres pour la méthode '{methodName}' est incorrect.");
}
catch (Exception ex)
{
throw new Exception($"Erreur lors de l'invocation de la méthode '{methodName}': {ex.Message}", ex);
}
}
/// <summary>
/// Exécute une méthode du service Web qui ne retourne pas de valeur.
/// </summary>
/// <param name="methodName">Le nom de la méthode à exécuter.</param>
/// <param name="parameters">Les paramètres à passer à la méthode.</param>
public void InvokeNonQueryMethod(string methodName, object[] parameters)
{
if (this.proxyType == null)
{
throw new InvalidOperationException($"Le type proxy pour le service '{this.serviceName}' n'a pas pu être chargé.");
}
MethodInfo method = this.proxyType.GetMethod(methodName);
if (method == null)
{
throw new MissingMethodException($"La méthode '{methodName}' n'a pas été trouvée dans le service Web.");
}
try
{
if (this.serviceInstance == null)
{
this.serviceInstance = Activator.CreateInstance(this.proxyType);
}
method.Invoke(this.serviceInstance, parameters);
}
catch (TargetParameterCountException)
{
throw new ArgumentException($"Le nombre de paramètres pour la méthode '{methodName}' est incorrect.");
}
catch (Exception ex)
{
throw new Exception($"Erreur lors de l'invocation de la méthode '{methodName}': {ex.Message}", ex);
}
}
private void GenerateAndLoadAssembly()
{
if (File.Exists(this.assemblyPath))
{
LoadProxyType();
return;
}
try
{
// Télécharger et analyser le WSDL
WebClient webClient = new WebClient();
using (Stream stream = webClient.OpenRead(this.wsdlUrl))
{
ServiceDescription serviceDescription = ServiceDescription.Read(stream);
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap";
importer.Style = ServiceDescriptionImportStyle.Client;
importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;
importer.AddServiceDescription(serviceDescription, null, null);
// Générer le code source du proxy
CodeNamespace codeNamespace = new CodeNamespace(this.assemblyName);
CodeCompileUnit compileUnit = new CodeCompileUnit();
compileUnit.Namespaces.Add(codeNamespace);
ResolveImports(this.wsdlUrl, importer);
ServiceDescriptionImportWarnings warnings = importer.Import(codeNamespace, compileUnit);
if (warnings != 0)
{
Console.WriteLine($"Avertissements lors de l'importation du WSDL: {warnings}");
}
// Compiler le code source en DLL
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters compilerParameters = new CompilerParameters();
compilerParameters.ReferencedAssemblies.Add("System.dll");
compilerParameters.ReferencedAssemblies.Add("System.Xml.dll");
compilerParameters.ReferencedAssemblies.Add("System.Web.Services.dll");
compilerParameters.ReferencedAssemblies.Add("System.Data.dll"); // Assurez-vous que cette référence est nécessaire
compilerParameters.GenerateExecutable = false;
compilerParameters.GenerateInMemory = false;
compilerParameters.IncludeDebugInformation = false;
compilerParameters.OutputAssembly = this.assemblyPath;
CompilerResults results = provider.CompileAssemblyFromDom(compilerParameters, compileUnit);
if (results.Errors.HasErrors)
{
StringBuilder errorMessages = new StringBuilder($"Erreurs de compilation ({results.Errors.Count}):\n");
foreach (CompilerError error in results.Errors)
{
errorMessages.Append($"Ligne {error.Line}: {error.ErrorText}\n");
}
throw new Exception(errorMessages.ToString());
}
}
}
catch (Exception ex)
{
// Gérer les erreurs réseau ou de parsing WSDL, éventuellement ne pas lancer d'exception si on est hors ligne
Console.WriteLine($"Erreur lors de la génération de l'assembly : {ex.Message}");
// Optionnellement : lancer une exception si la connectivité est requise
// throw new Exception($"Impossible de générer l'assembly pour le service '{this.serviceName}'. Vérifiez la connectivité réseau et l'URL WSDL.", ex);
}
finally
{
LoadProxyType();
}
}
private void LoadProxyType()
{
if (File.Exists(this.assemblyPath))
{
try
{
Assembly serviceAssembly = Assembly.LoadFrom(this.assemblyPath);
Type[] types = serviceAssembly.GetTypes();
string serviceClientTypeName = "";
foreach (Type type in types)
{
// Chercher le type qui hérite de SoapHttpClientProtocol (ou similaire)
if (type.BaseType != null && type.BaseType.Name == "SoapHttpClientProtocol")
{
serviceClientTypeName = type.Name;
break;
}
}
if (!string.IsNullOrEmpty(serviceClientTypeName))
{
this.proxyType = serviceAssembly.GetType($"{this.assemblyName}.{serviceClientTypeName}");
}
else
{
throw new TypeLoadException("Impossible de trouver le type client du service Web dans l'assembly générée.");
}
}
catch(Exception ex)
{
Console.WriteLine($"Erreur lors du chargement de l'assembly proxy : {ex.Message}");
// Supprimer le fichier DLL corrompu si nécessaire
if (File.Exists(this.assemblyPath)) File.Delete(this.assemblyPath);
this.proxyType = null; // Réinitialiser pour forcer la régénération si nécessaire
}
}
}
private void ResolveImports(string baseWsdlUrl, ServiceDescriptionImporter importer)
{
try
{
DiscoveryClientProtocol discoveryClient = new DiscoveryClientProtocol();
discoveryClient.DiscoverAny(baseWsdlUrl);
discoveryClient.ResolveAll();
foreach (object document in discoveryClient.Documents.Values)
{
if (document is ServiceDescription serviceDesc)
{
importer.AddServiceDescription(serviceDesc, null, null);
}
else if (document is XmlSchema schema)
{
importer.Schemas.Add(schema);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Erreur lors de la résolution des imports WSDL : {ex.Message}");
}
}
private string GetMd5Hash(string input)
{
using (MD5 md5Hash = MD5.Create())
{
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
return sBuilder.ToString();
}
}
private static string GetServiceNameFromUrl(string wsdlUrl)
{
Uri uri = new Uri(wsdlUrl);
string path = uri.AbsolutePath;
string fileName = Path.GetFileNameWithoutExtension(path);
return fileName.Replace("?wsdl", ""); // Nettoyer le nom du fichier
}
}
L'exemple d'utilisation suivant montre comment appeler une méthode nommée interfaceTest en utilisant la classe DynamicWebServiceProxy :
// Déclaration d'une variable globale ou statique pour conserver l'instance du proxy si besoin
// pour éviter la régénération fréquente. Par exemple, dans une classe statique comme TrhaConst.
// public static DynamicWebServiceProxy webServiceClient;
private void CallServiceButton_Click(object sender, EventArgs e)
{
string ipAddress = "10.80.16.38";
string port = "8081";
string wsdlAddress = $"http://{ipAddress}:{port}/phyServer/services/ImportDataService?wsdl";
string serviceObjectName = "MyImportService"; // Nom logique pour le proxy
try
{
// Créer une instance du proxy dynamique.
// Il est conseillé de mettre en cache cette instance si le service est appelé plusieurs fois.
DynamicWebServiceProxy serviceProxy = new DynamicWebServiceProxy(wsdlAddress, serviceObjectName);
// Préparer les paramètres pour la méthode du service
object[] methodParams = new object[1];
methodParams[0] = "dynamic_call_data";
// Nom de la méthode à appeler sur le service Web
string methodName = "interfaceTest";
// Appeler la méthode et récupérer le résultat
object result = serviceProxy.InvokeMethod(methodName, methodParams);
if (result != null)
{
MessageBox.Show($"Résultat du service Web : {result.ToString()}");
}
else
{
MessageBox.Show("La méthode du service Web a été appelée mais n'a retourné aucune valeur.");
}
}
catch (Exception ex)
{
MessageBox.Show($"Erreur lors de l'appel du service Web : {ex.Message}", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}