2009-02-18 8 views
12

Ho iniziato a scrivere un'interfaccia per le API del servizio Web di FedEx. Hanno 3 diverse API a cui sono interessato; Valuta, spedisci e traccia. Sto generando i proxy di servizio con SvcUtil.exe.Tipi di coercizione in diversi spazi dei nomi con layout identico in C#

I diversi endpoint del servizio sono specificati da FedEx nei rispettivi file WSDL. Ogni endpoint del servizio ha il proprio namespace XML (ad esempio http://fedex.com/ws/rate/v5 e http://fedex.com/ws/ship/v5)

Gli endpoint del servizio faccio uso di un bel paio di tipi identici come l'indirizzo, misure, peso, AuthenticationDetail, ClientDetail, ecc ...

E qui è dove si trova il problema, posso fornire tutti i file WSDL contemporaneamente a SvcUtil.exe e normalmente convergerebbe tutti i tipi identici in un singolo tipo condiviso, ma poiché ciascuno dei servizi di FedEx è nel proprio spazio dei nomi, e redeclare questi tipi in ogni file WSDL sotto quello spazio dei nomi, invece, sono un indirizzo, indirizzo1 e indirizzo2 uno per ogni spazio dei nomi.

Per risolvere il problema, ciò che faccio ora è eseguire separatamente ogni WSDL tramite svcutil e inserirli ciascuno nel proprio spazio dei nomi .NET (ad esempio FedEx.Rate, FedEx.Ship, FedEx.Track). Il problema con questo è che ora ho un tipo di indirizzo distinto in ogni spazio dei nomi (FedEx.Rate.Address, FedEx.Ship.Address).

Ciò rende difficile generalizzare il codice utilizzato tra i servizi come un metodo factory GetAuthenticationDetail() in modo da non dover ripetere quel codice in ogni luogo in cui utilizzo i diversi servizi.

Esiste un modo in C# per forzare FedEx.Rate.Address a FedEx.Ship.Address?

+0

+1 per la domanda. Sto lottando con lo stesso problema e non ho mai trovato una soluzione piacevole. –

+0

Ottima domanda; Ho pensato che stavo importando il WSDL in modo errato, finché non ho guardato più vicino e sì, hanno davvero assegnato diversi spazi dei nomi a tipi identici. –

risposta

8

Se i tipi sono identici e si ha il controllo sulle classi di origine, è possibile definire un conversion operator nella classe e qualsiasi funzione che prende uno Rate.Address assumerà automaticamente un Ship.Address. Ad esempio:

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

Il mio C# è un po 'arrugginito ma spero che tu abbia l'idea.

+0

C'è un problema con questo approccio. Ad esempio, se il WSDL cambia (viene aggiunta qualche proprietà) e i proxy vengono rigenerati con svcutil.exe non è necessario dimenticare di aggiornare il metodo implicito di oprator o si potrebbe ottenere un comportamento strano durante il runtime. –

+0

hmm, mi piace questo approccio e ho persino letto degli operatori di conversione in C# un paio di settimane fa. Lasciami fare un tentativo. L'aspetto positivo dei servizi Web di FedEx è che quando rilasciano una nuova versione il vecchio funziona ancora a tempo indeterminato. Quindi il commento di Darin non mi causerà troppi problemi – joshperry

+0

Forse vedrò di implementare il corpo dell'operatore usando il reflection, quindi se il WSDL lo cambia dovrebbe funzionare automaticamente solo se hai gli operatori di conversione corretti in posto. – joshperry

1

si potrebbe usare l'overloading degli operatori attraverso la creazione di una propria implementazione di indirizzo o utilizzare uno dei tipi di stabili come una proprietà

un esempio: Indirizzo1 e Indirizzo2 seguito sarebbero rispettivamente

tua Rate.Address e Ship.Address
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'approccio è lo stesso come il post di cui sopra, l'implementazione è in una classe separata questo è tutto

+0

Questa non è una cattiva idea se non che è molto lavoro duplicare tutti i tipi già esistenti, ho pensato di scrivere un generatore di codice che farebbe qualcosa di simile a questo, ma i generatori di codice personalizzati di Visual Studio sono difficili da implementare in un ambiente di squadra. – joshperry

1

sono quelle classi generate definiti come "parziale"? In tal caso, è possibile estenderli in un file diverso ed estrarre un'interfaccia e lasciarla implementare da tutte le classi Address.

+0

Questo ti consentirà di trattare i tipi in modo uniforme nel tuo codice, ma questo non ti permetterà di passare FedEx.Rate.Address al servizio FedEx.Ship. –

7

Quindi ecco come ho implementato gli operatori di conversione impliciti utilizzando la riflessione. SvcUtil crea classi parziali quindi ho aggiunto un operatore di conversione implicito per ogni direzione della conversione, quindi nel codice client puoi semplicemente digitare Type1 = Type2.

In questo frammento WebAuthenticationCredentials è una proprietà di WebAuthenticationDetails così mentre iterando le proprietà dell'oggetto sorgente se i tipi non sono gli stessi (built-in) controlla il nome dei tipi (senza lo spazio dei nomi) e richiama in modo ricorsivo la copia funziona con quelle proprietà.

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