2009-02-18 8 views
12

Empecé a escribir una interfaz para las API de servicio web de FedEx. Tienen 3 API diferentes que me interesan; Tasa, envío y seguimiento. Estoy generando los proxies de servicio con SvcUtil.exe.Tipos de coerción en diferentes espacios de nombres con diseño idéntico en C#

FedEx especifica los diferentes puntos finales del servicio en sus propios archivos WSDL. Cada extremo de servicio tiene su propio espacio de nombres XML (por ejemplo http://fedex.com/ws/rate/v5 y http://fedex.com/ws/ship/v5)

Los puntos finales de servicios hacen uso de un buen número de tipos idénticos, tales como la dirección, de Medidas, peso, AuthenticationDetail, ClientDetail, etc ...

Y aquí es donde reside el problema, puedo proporcionar todos los archivos WSDL al mismo tiempo a SvcUtil.exe y normalmente combinaría cualquier tipo idéntico en un único tipo compartido, pero como cada uno de los servicios de FedEx está en su propio espacio de nombres, y redeclaran estos tipos en cada archivo WSDL bajo ese espacio de nombre con el que termino en cambio es una Dirección, una Dirección1 y una Dirección una para cada espacio de nombres.

Para resolver ese problema, lo que hago ahora es ejecutar cada WSDL a través de svcutil por separado y ponerlos cada uno en su propio espacio de nombres .NET (por ejemplo, FedEx.Rate, FedEx.Ship, FedEx.Track). El problema con esto es que ahora tengo un tipo de dirección distinto en cada espacio de nombre (Fedex.Rate.Address, FedEx.Ship.Address).

Esto dificulta la generalización del código utilizado entre los servicios, como el método de fábrica GetAuthenticationDetail(), por lo que no tengo que repetir ese código en todos los lugares donde utilizo los diferentes servicios.

¿Hay alguna forma en C# de forzar FedEx.Rate.Address a FedEx.Ship.Address?

+0

+1 para la pregunta. Estoy luchando con el mismo problema y nunca encontré una buena solución. –

+0

Gran pregunta; Pensé que había estado importando el WSDL incorrectamente, hasta que miré más de cerca y sí, realmente asignaron diferentes espacios de nombres a tipos idénticos. –

Respuesta

8

Si los tipos son idénticos, y usted tiene control sobre las clases de origen, puede definir un conversion operator en la clase, y cualquier función que tome un Rate.Address también tomará automáticamente un Ship.Address. Por ejemplo:

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

Mi C# está un poco oxidado pero espero que entienda la idea.

+0

Hay un problema con este enfoque. Por ejemplo, si el WSDL cambia (se agrega una propiedad) y los proxies se vuelven a generar con svcutil.exe, no tiene que olvidarse de actualizar el método implicit oprator o puede obtener algún comportamiento extraño en el tiempo de ejecución. –

+0

hmm, me gusta este enfoque, e incluso leí acerca de los operadores de conversión en C# hace un par de semanas. Déjame intentarlo. Lo bueno de los servicios web de FedEx es que cuando lanzan una nueva versión, la antigua funciona indefinidamente. Entonces, el comentario de Darin no me va a causar demasiados problemas. – joshperry

+0

Tal vez busque implementar el cuerpo del operador usando la reflexión; luego, si el WSDL cambia, debería funcionar automáticamente siempre y cuando tenga los operadores de conversión correctos en lugar. – joshperry

1

podría utilizar la sobrecarga de operadores mediante la creación de su propia implementación de Dirección o utilizar uno de los tipos estables como una propiedad

un ejemplo: Dirección 1 Dirección 2 y más adelante sería su Rate.Address y Ship.Address respectivamente

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

Editar: el enfoque es el mismo que el post anterior, la aplicación está en una clase separada eso es todo

+0

Esto no es una mala idea, excepto que es mucho trabajo duplicar todos los tipos que ya existen, he contemplado escribir un generador de código que haría algo similar a esto, pero los generadores de código de Visual Studio personalizados son difíciles de implementar en un ambiente de equipo – joshperry

1

son aquellos generados clases definidas como "parcial"? Si es así, podría extenderlos en un archivo diferente y extraer una interfaz y dejar que sea implementada por todas las clases de direcciones.

+0

Esto le permitirá tratar los tipos de manera uniforme en su código, pero esto no le permitirá pasar la FedEx.Rate.Address al servicio FedEx.Ship. –

7

Así es como implementé los operadores de conversión implícita utilizando la reflexión. SvcUtil crea clases parciales, así que agregué un operador de conversión implícito para cada dirección de la conversión, así que en el código del cliente puede escribir Type1 = Type2.

En este fragmento, WebAuthenticationCredentials es una propiedad de WebAuthenticationDetails por lo que al iterar las propiedades del objeto fuente si los tipos no son iguales (integrados) comprueba el nombre de los tipos (sin el espacio de nombres) y recursivamente llama a la copia funcionar con esas propiedades.

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