简体   繁体   English

无法访问我在泛型方法中用作类型参数的 class 的基数 class 的属性

[英]Can't access properties of the base class of the class I am using as the type argument in a generic method

Background背景

I have a third party system that offers SOAP APIs.我有一个提供 SOAP API 的第三方系统。 The class structure of their WSDLs leaves a lot to be desired in general, but one area I'm struggling with is streamlining the retrieval of bindings.他们的 WSDL 的 class 结构一般来说还有很多不足之处,但我正在努力解决的一个领域是简化绑定的检索。 Their sample code has around 20 of these methods, one for each endpoint/class/interface:他们的示例代码有大约 20 个这样的方法,每个端点/类/接口一个:

public static ICreateObjectPortType GetCreateObjectHttpBinding()
{
    var servicePort = new CreateObjectPortTypeClient();

    servicePort.Endpoint.ListenUri = GetServiceUrl("ReadObject");

    servicePort.ClientCredentials.UserName.UserName = SDK.Username;
    servicePort.ClientCredentials.UserName.Password = SDK.Password;

    SetupHttpHeader(servicePort.InnerChannel);
    return servicePort;
}

My primary goal is to convert these into a generic method that I can call with just a parameter or two and return the class. (You'll also see in my current code that I'm implementing some dynamic binding code. This is because I have multiple companies to access and their system structure requires bindings per company. This is not relevant to the question.)我的主要目标是将它们转换成一个通用方法,我可以只用一两个参数调用它并返回 class。(你还会在我当前的代码中看到我正在实现一些动态绑定代码。这是因为我有多家公司可以访问,他们的系统结构需要每个公司绑定。这与问题无关。)

The basic structure of all the classes and interfaces is as follows.所有类和接口的基本结构如下。 I have included only the constructor I'm targeting.我只包含了我的目标构造函数。 These are defined in the stub classes created from the WSDLs, and not something I want to directly edit if possible.:这些是在从 WSDL 创建的存根类中定义的,如果可能,我不想直接编辑它们。

Interfaces 接口
public interface ICreateObjectPortType { }
Classes 班级
public static void SetupHttpHeader(IClientChannel serviceChannel)
{
    using (new OperationContextScope(serviceChannel))
    {
        // Add a HTTP Header to an outgoing request
        var requestMessage = new HttpRequestMessageProperty();
        requestMessage.Headers["Authorization"] = "Basic " + System.Convert.ToBase64String(Encoding.UTF8.GetBytes(SDK.Username + ":" + SDK.Password));
        OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
    }
}

Here are examples of the other interfaces and classes declared in the stubs.以下是存根中声明的其他接口和类的示例。 The only common thing between all of them is all of the classes inherit from ClientBase<interface> and interface :它们之间唯一的共同点是所有类都继承自ClientBase<interface>interface

  • IReadObjectPortType and ReadObjectPortTypeClient IReadObjectPortTypeReadObjectPortTypeClient
  • IUpdateObjectPortType and UpdateObjectPortTypeClient IUpdateObjectPortTypeUpdateObjectPortTypeClient
  • IFindObjectsPortType and FindObjectsPortTypeClient IFindObjectsPortTypeFindObjectsPortTypeClient

Here is the SetupHttpHeader method defined in their sample code.这是他们的示例代码中定义的SetupHttpHeader方法。 I haven't checked to see if it's actually needed yet, but I'm including it for completeness:我还没有检查它是否真的需要,但为了完整性我把它包括在内:

 public static void SetupHttpHeader(IClientChannel serviceChannel) { using (new OperationContextScope(serviceChannel)) { // Add a HTTP Header to an outgoing request var requestMessage = new HttpRequestMessageProperty(); requestMessage.Headers["Authorization"] = "Basic " + System.Convert.ToBase64String(Encoding.UTF8.GetBytes(SDK.Username + ":" + SDK.Password)); OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage; } }

My Code我的代码

And lastly for code samples, here is my stab at a generic method, the supporting credential setting method, and what it looks like to use them:最后,对于代码示例,这是我对通用方法的尝试、支持的凭据设置方法,以及使用它们的样子:

 public static T ConfigureServicePort<T>(Func<string,EndpointAddress, T> test, string serviceName) where T: class { var endpointName = serviceName + "HttpPort"; var endpointAddress = new EndpointAddress($"https://hcmiller.myprintdesk.com:443/rpc/company:public/services/{serviceName}"); var servicePort = test(endpointName, endpointAddress); return servicePort; } public static void SetClientCredentials(this ClientCredentials creds) { creds.UserName.UserName = SDK.Username; creds.UserName.Password = SDK.Password; } public static ICreateObjectPortType GetCreateObjectHttpBinding() { var servicePort = ConfigureServicePort((x, y) => new CreateObjectPortTypeClient(x, y), "CreateObject"); servicePort.ClientCredentials.SetClientCredentials(); SetupHttpHeader(servicePort.InnerChannel); return servicePort; }

As you can see, I'm not quite there yet.如您所见,我还没有完成。 I have successfully implemented a function parameter in the method in order to call a constructor on T with parameters.我已经成功地在方法中实现了一个 function 参数,以便使用参数调用T上的构造函数。 It's far more open than I prefer my generics to be, but I don't know of another way to allow this while also restricting what T can be.它比我更喜欢我的 generics 更开放,但我不知道另一种方法可以允许它同时限制T可以是什么。

The Question问题

My problem right now is getting the setting of ClientCredentials and calling of SetupHttpHeader to happen in that one generic method as well.我现在的问题是让 ClientCredentials 的设置和ClientCredentials的调用SetupHttpHeader那个通用方法中发生。 Given that there is no base interface for the classes, and the only common ground they have is ClientBase<T> I haven't found a way to treat servicePort inside ConfigureServicePort as something that inherits from ClientBase<T> to get at the necessary properties.鉴于这些类没有基本接口,并且它们唯一的共同点是ClientBase<T>我还没有找到一种方法来将ConfigureServicePort中的servicePort视为从ClientBase<T>继承的东西以获取必要的属性.

How can I get it so that ConfigureServicePort<T> can treat its T like a ClientBase<T> so that I can get to the properties I need to configure?我怎样才能得到它,以便ConfigureServicePort<T>可以像对待ClientBase<T>一样对待它的T ,以便我可以获得我需要配置的属性?

I cannot test this as I'd need an actual service, but assuming that you can do this:我无法对此进行测试,因为我需要实际服务,但假设您可以这样做:

public CreateObjectPortTypeClient() { }

which can fail if some conditions aren't met (as specified in the ClientBase<T> constructor):如果不满足某些条件(如ClientBase<T>构造函数中指定的那样),它可能会失败:

//
// Summary:
//     Initializes a new instance of the System.ServiceModel.ClientBase`1 class using
//     the default target endpoint from the application configuration file.
//
// Exceptions:
//   T:System.InvalidOperationException:
//     Either there is no default endpoint information in the configuration file, more
//     than one endpoint in the file, or no configuration file.

If that works for you, you can make your life quite easy.如果这对你有用,你可以让你的生活变得很轻松。

Let's simplify the situation with 2 services:让我们用 2 项服务来简化情况:

public interface IService1 { }
public class Service1 : ClientBase<IService1>, IService1 { }

public interface IService2 { }
public class Service2 : ClientBase<IService2>, IService2 { }

then you can just use this:那么你可以使用这个:

public static TClient ConfigureServicePort<TClient, TService>(string serviceName)
    where TClient : ClientBase<TService>, new()
    where TService : class
{
    var servicePort = new TClient();
    servicePort.Endpoint.Address = new EndpointAddress($"https://hcmiller.myprintdesk.com:443/rpc/company:public/services/{serviceName}");
    servicePort.Endpoint.Name = serviceName + "HttpPort";

    servicePort.ClientCredentials.SetClientCredentials();
    SetupHttpHeader(servicePort.InnerChannel);

    return servicePort;
}

and then call it simply with:然后简单地调用它:

var service1Client = X.ConfigureServicePort<Service1, IService1>("Service1");
var service2Client = X.ConfigureServicePort<Service2, IService2>("Service2");

Notice that the two generic parameters are needed as you need to satisfy the generic constraint set by ClientBase<T> .请注意,需要两个通用参数,因为您需要满足ClientBase<T>设置的通用约束。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM