繁体   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

背景

我有一个提供 SOAP API 的第三方系统。 他们的 WSDL 的 class 结构一般来说还有很多不足之处,但我正在努力解决的一个领域是简化绑定的检索。 他们的示例代码有大约 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;
}

我的主要目标是将它们转换成一个通用方法,我可以只用一两个参数调用它并返回 class。(你还会在我当前的代码中看到我正在实现一些动态绑定代码。这是因为我有多家公司可以访问,他们的系统结构需要每个公司绑定。这与问题无关。)

所有类和接口的基本结构如下。 我只包含了我的目标构造函数。 这些是在从 WSDL 创建的存根类中定义的,如果可能,我不想直接编辑它们。

接口
public interface ICreateObjectPortType { }
班级
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;
    }
}

以下是存根中声明的其他接口和类的示例。 它们之间唯一的共同点是所有类都继承自ClientBase<interface>interface

  • IReadObjectPortTypeReadObjectPortTypeClient
  • IUpdateObjectPortTypeUpdateObjectPortTypeClient
  • IFindObjectsPortTypeFindObjectsPortTypeClient

这是他们的示例代码中定义的SetupHttpHeader方法。 我还没有检查它是否真的需要,但为了完整性我把它包括在内:

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

我的代码

最后,对于代码示例,这是我对通用方法的尝试、支持的凭据设置方法,以及使用它们的样子:

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

如您所见,我还没有完成。 我已经成功地在方法中实现了一个 function 参数,以便使用参数调用T上的构造函数。 它比我更喜欢我的 generics 更开放,但我不知道另一种方法可以允许它同时限制T可以是什么。

问题

我现在的问题是让 ClientCredentials 的设置和ClientCredentials的调用SetupHttpHeader那个通用方法中发生。 鉴于这些类没有基本接口,并且它们唯一的共同点是ClientBase<T>我还没有找到一种方法来将ConfigureServicePort中的servicePort视为从ClientBase<T>继承的东西以获取必要的属性.

我怎样才能得到它,以便ConfigureServicePort<T>可以像对待ClientBase<T>一样对待它的T ,以便我可以获得我需要配置的属性?

我无法对此进行测试,因为我需要实际服务,但假设您可以这样做:

public CreateObjectPortTypeClient() { }

如果不满足某些条件(如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.

如果这对你有用,你可以让你的生活变得很轻松。

让我们用 2 项服务来简化情况:

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

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

那么你可以使用这个:

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

然后简单地调用它:

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

请注意,需要两个通用参数,因为您需要满足ClientBase<T>设置的通用约束。

暂无
暂无

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

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