2009-02-18 6 views
12

J'ai commencé à écrire une interface pour les API webservice de FedEx. Ils ont 3 API différentes qui m'intéressent; Rate, Ship et Track. Je génère les proxies de service avec SvcUtil.exe.Types de coercition dans différents espaces de noms avec disposition identique en C#

Les différents points de terminaison de service sont chacun spécifiés par FedEx dans leurs propres fichiers WSDL. Chaque point de terminaison de service a son propre espace de noms XML (par exemple http://fedex.com/ws/rate/v5 et http://fedex.com/ws/ship/v5)

Les points d'extrémité de service n'utilisent pas mal de types identiques tels que l'adresse, mesures, poids, AuthenticationDetail, ClientDetail, etc ...

Et ici est où le problème réside, je peux fournir tous les fichiers WSDL en même temps à SvcUtil.exe et normalement il fusionnerait tous les types identiques en un seul type partagé, mais puisque chacun des services de FedEx sont dans leur propre espace de noms, et ils redéclare ces types dans chaque fichier WSDL sous cet espace de noms ce que je me retrouve à la place est une Adresse, Adresse1, et Adresse2 un pour chaque espace de noms. Pour résoudre ce problème, ce que je fais maintenant est d'exécuter chaque WSDL séparément via svcutil et de les placer chacun dans leur propre espace de noms .NET (par exemple FedEx.Rate, FedEx.Ship, FedEx.Track). Le problème avec ceci est que maintenant j'ai un type d'adresse distinct dans chaque espace de noms (Fedex.Rate.Address, FedEx.Ship.Address). Cela rend difficile la généralisation du code utilisé entre les services comme une méthode d'usine GetAuthenticationDetail(), donc je n'ai pas besoin de répéter ce code à chaque endroit où j'utilise les différents services.

Y a-t-il un moyen en C# de contraindre FedEx.Rate.Address à FedEx.Ship.Address?

+0

+1 pour la question. Je suis aux prises avec le même problème et je n'ai jamais trouvé une bonne solution. –

+0

Bonne question; Je pensais que j'avais importé le WSDL mal, jusqu'à ce que je regarde de plus près et oui, ils ont vraiment assigné des espaces de noms différents à des types identiques. –

Répondre

8

Si les types sont identiques et que vous avez le contrôle sur les classes source, vous pouvez définir un conversion operator dans la classe, et toute fonction qui prend un Rate.Address prendra automatiquement un Ship.Address. Par exemple:

namespace Rate { 
    class Address { 
     string Street; 
     string City; 
     // ... 

     public static implicit operator Ship.Address(Rate.Address addr) { 
      Ship.Address ret; 
      ret.Street = addr.Street; 
      ret.City = addr.City; 
      // ... 

      return ret; 
     } 
    } 
} 

Mon C# est un peu rouillé mais j'espère que vous avez l'idée.

+0

Il y a un problème avec cette approche. Par exemple, si le WSDL change (certaines propriétés sont ajoutées) et que les proxies sont régénérés avec svcutil.exe, vous ne devez pas oublier de mettre à jour la méthode de l'oprateur implicite ou vous pourriez avoir un comportement étrange à l'exécution. –

+0

hmm, j'aime cette approche, et j'ai même lu à propos des opérateurs de conversion en C# il y a quelques semaines. Laissez-moi essayer. La bonne chose à propos des services Web de FedEx est que lorsqu'ils lancent une nouvelle version, l'ancien fonctionne indéfiniment. Donc le commentaire de darin ne va pas me causer trop de problèmes – joshperry

+0

Peut-être que je vais regarder comment implémenter le corps de l'opérateur en utilisant la réflexion, alors si le WSDL change, il devrait fonctionner automatiquement tant que vous avez les bons opérateurs de conversion endroit. – joshperry

1

vous pouvez utiliser la surcharge opérateur en créant votre propre implémentation d'adresse ou d'utiliser l'un des types stables comme une propriété

un exemple: Adresse1 et Address2 inférieures soit votre Rate.Address et Ship.Address respectivement

class Address1 
{ 
    public string name = "Address1"; 
} 
class Address2 
{ 
    public string name = "Address2"; 
} 

class GenericAddress 
{ 
    public string name = "GenericAddress"; 
    public static implicit operator GenericAddress(Address1 a) 
    { 
     GenericAddress p = new GenericAddress(); p.name = a.name; return p; 
    } 
    public static implicit operator GenericAddress(Address2 a) 
    { 
     GenericAddress p = new GenericAddress(); p.name = a.name; return p; 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     PrintName(new Address1());//prints address1 
     PrintName(new Address2());//prints address2 
    } 

    static void PrintName(GenericAddress a) 
    { 
     Console.WriteLine(a.name); 
    } 
} 

Edit: l'approche est la même que le poste ci-dessus, la mise en œuvre est dans une classe séparée c'est tout

+0

Ce n'est pas une mauvaise idée, sauf qu'il est beaucoup de travail pour dupliquer tous les types qui existent déjà, j'ai envisagé d'écrire un générateur de code qui ferait quelque chose de similaire, mais les générateurs de code Visual Studio personnalisés sont difficiles à implémenter dans un environnement d'équipe. – joshperry

1

sont ceux des classes générées définies comme « partielle »? Si c'est le cas, vous pouvez les étendre dans un fichier différent et extraire une interface et la laisser être implémentée par toutes les classes Address.

+0

Cela vous permettra de traiter les types uniformément dans votre code, mais cela ne vous laissera pas passer le FedEx.Rate.Address au service FedEx.Ship. –

7

Voici donc comment j'ai implémenté les opérateurs de conversion implicites en utilisant la réflexion. SvcUtil crée des classes partielles, j'ai donc ajouté un opérateur de conversion implicite pour chaque direction de la conversion, donc dans le code client, vous pouvez simplement taper Type1 = Type2.

Dans cet extrait WebAuthenticationCredentials est une propriété de WebAuthenticationDetails de sorte que lors de l'itération des propriétés de l'objet source si les types arent les mêmes (built-ins) il vérifie le nom des types (sans l'espace de noms) et appelle récursivement la copie fonctionner avec ces propriétés.

internal class ReflectionCopy 
{ 
    public static ToType Copy<ToType>(object from) where ToType : new() 
    { 
     return (ToType)Copy(typeof(ToType), from); 
    } 

    public static object Copy(Type totype, object from) 
    { 
     object to = Activator.CreateInstance(totype); 

     PropertyInfo[] tpis = totype.GetProperties(BindingFlags.Public | BindingFlags.Instance); 
     PropertyInfo[] fpis = from.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); 

     // Go through each property on the "to" object 
     Array.ForEach(tpis, tpi => 
     { 
      // Find a matching property by name on the "from" object 
      PropertyInfo fpi = Array.Find(fpis, pi => pi.Name == tpi.Name); 
      if (fpi != null) 
      { 
       // Do the source and destination have identical types (built-ins)? 
       if (fpi.PropertyType == tpi.PropertyType) 
       { 
        // Transfer the value 
        tpi.SetValue(to, fpi.GetValue(from, null), null); 
       } 
       else 
       { 
        // If type names are the same (ignoring namespace) copy them recursively 
        if (fpi.PropertyType.Name == tpi.PropertyType.Name) 
         tpi.SetValue(to, Copy(fpi.PropertyType, tpi.GetValue(from, null)), null); 
       } 
      } 
     }); 

     return to; 
    } 
} 

namespace Rate 
{ 
    partial class WebAuthenticationDetail 
    { 
     public static implicit operator Ship.WebAuthenticationDetail(WebAuthenticationDetail from) 
     { 
      return ReflectionCopy.Copy<Ship.WebAuthenticationDetail>(from); 
     } 
    } 

    partial class WebAuthenticationCredential 
    { 
     public static implicit operator Ship.WebAuthenticationCredential(WebAuthenticationCredential from) 
     { 
      return ReflectionCopy.Copy<Ship.WebAuthenticationCredential>(from); 
     } 
    } 
} 

namespace Ship 
{ 
    partial class WebAuthenticationDetail 
    { 
     public static implicit operator Rate.WebAuthenticationDetail(WebAuthenticationDetail from) 
     { 
      return ReflectionCopy.Copy<Rate.WebAuthenticationDetail>(from); 
     } 
    } 

    partial class WebAuthenticationCredential 
    { 
     public static implicit operator Rate.WebAuthenticationCredential(WebAuthenticationCredential from) 
     { 
      return ReflectionCopy.Copy<Rate.WebAuthenticationCredential>(from); 
     } 
    } 
} 
Questions connexes