在 C# 中使用相同布局强制不同命名空间中的类型

发布于 2024-07-13 16:09:49 字数 927 浏览 10 评论 0原文

我已经开始为 FedEx 的 Web 服务 API 编写接口。 他们有 3 个我感兴趣的不同 API; 费率、运送和追踪。 我正在使用 SvcUtil.exe 生成服务代理。

不同的服务端点均由 FedEx 在其自己的 WSDL 文件中指定。 每个服务端点都有自己的 xml 命名空间(例如 http://fedex.com/ws/rate/v5 和 http://fedex.com/ws/ship/v5)

服务端点执行使用相当多相同的类型,例如地址、测量、重量、身份验证详细信息、客户端详细信息等...

这就是问题所在,我可以同时向 SvcUtil.exe 提供所有 WSDL 文件,通常它会合并将任何相同的类型转换为单个共享类型,但由于 FedEx 的每个服务都位于其自己的命名空间中,并且它们在该命名空间下的每个 WSDL 文件中重新声明这些类型,所以我最终得到的是一个 Address、Address1 和 Address2 一个命名空间。

为了解决这个问题,我现在要做的就是通过 svcutil 单独运行每个 WSDL,并将它们分别放在自己的 .NET 命名空间中(例如 FedEx.Rate、FedEx.Ship、FedEx.Track)。 问题在于,现在我在每个命名空间中都有不同的地址类型(Fedex.Rate.Address、FedEx.Ship.Address)。

这使得很难概括服务之间使用的代码,例如 GetAuthenticationDetail() 工厂方法,因此我不必在使用不同服务的每个地方重复该代码。

C# 有什么方法可以将 FedEx.Rate.Address 强制转换为 FedEx.Ship.Address 吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

逆夏时光 2024-07-20 16:09:49

如果类型相同,并且您可以控制源类,则可以定义 类中的转换运算符,任何采用 Rate.Address 的函数也将自动采用 Ship.Address。 例如:

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

我的 C# 有点生疏,但我希望你能明白。

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.

岁月流歌 2024-07-20 16:09:49

以下是我如何使用反射实现隐式转换运算符。 SvcUtil 创建部分类,因此我为每个转换方向添加了一个隐式转换运算符,因此在客户端代码中您只需键入 Type1 = Type2

在此代码片段中,WebAuthenticationCredentials 是 WebAuthenticationDetails 的属性,因此在迭代源对象的属性时,如果类型不相同(内置),它会检查类型的名称(不带命名空间)并使用这些属性递归调用复制函数。

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

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);
        }
    }
}
深府石板幽径 2024-07-20 16:09:49

您可以通过创建自己的 Address 实现来使用运算符重载,或者使用其中一种稳定类型作为属性,

例如:下面的 Address1 和 Address2 分别是您的 Rate.Address 和 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);
    }
}

编辑:方法与帖子相同上面,实现是在一个单独的类中,仅此而已

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

冧九 2024-07-20 16:09:49

这些生成的类是否定义为“部分”? 如果是这样,您可以在不同的文件中扩展它们并提取一个接口并让所有 Address 类实现它。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文