joshperry
joshperry

Reputation: 42257

Coerce types in different namespaces with Identical layout in C#

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

Answers (4)

joshperry
joshperry

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

flq
flq

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

user65157
user65157

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

zildjohn01
zildjohn01

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

Related Questions