Reputation: 42257
I've started writing an interface for FedEx's webservice APIs. They have 3 different APIs that I'm interested in; Rate, Ship, and Track. I am generating the service proxies with SvcUtil.exe.
The different service endpoints are each specified by FedEx in their own WSDL files. Each service endpoint has it's own xml namespace (e.g. http://fedex.com/ws/rate/v5 and http://fedex.com/ws/ship/v5)
The service endpoints do use quite a few identical types such as Address, Measurements, Weight, AuthenticationDetail, ClientDetail, etc...
And here is where the problem lies, I can provide all the WSDL files at the same time to SvcUtil.exe and normally it would coalesce any identical types into a single shared type, but since each of FedEx's services are in their own namespace, and they redeclare these types in each WSDL file under that namespace what I end up with instead is an Address, Address1, and Address2 one for each namespace.
To solve that issue, what I do now is to run each WSDL through svcutil separately and put them each in their own .NET namespace (e.g. FedEx.Rate, FedEx.Ship, FedEx.Track). The problem with this is that now I have a distinct address type in each namespace (Fedex.Rate.Address, FedEx.Ship.Address).
This makes it difficult to generalize the code used between the services like a GetAuthenticationDetail() factory method so I don't have to repeat that code in every place I use the different services.
Is there any way in C# to Coerce FedEx.Rate.Address to FedEx.Ship.Address?
Upvotes: 13
Views: 3182
Reputation: 42257
So here is how I implemented the implicit conversion operators using reflection. SvcUtil creates partial classes so I added an implicit conversion operator for each direction of the conversion so in the client code you can just type Type1 = Type2
.
In this snippet WebAuthenticationCredentials is a property of WebAuthenticationDetails so while iterating the properties of the source object if the types arent the same (built-ins) it checks the name of the types (without the namespace) and recursively calls the copy function with those properties.
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);
}
}
}
Upvotes: 7
Reputation: 22859
Are those generated classes defined as "partial" ? If so, you could extend them in a different file and extract an interface and let it be implemented by all Address classes.
Upvotes: 1
Reputation: 2415
you could use operator overloading by creating your own implementation of Address or use one of the stable types as a property
one example: Address1 and Address2 below would be your Rate.Address and Ship.Address respectively
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: the approach is the same as the post above, the implementation is in a seperate class thats all
Upvotes: 1
Reputation: 11515
If the types are identical, and you have control over the source classes, you can define a conversion operator in the class, and any function that takes a Rate.Address
will also automatically take a Ship.Address
. For example:
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;
}
}
}
My C# is a little rusty but I hope you get the idea.
Upvotes: 9