【问题标题】:Inheriting from a generic contract in WCF从 WCF 中的通用合同继承
【发布时间】:2010-10-28 14:04:51
【问题描述】:

更多 WCF 问题... :)

我所有的工作流程都实现了相同的 3 种方法。经过大量的复制和粘贴,我决定让它们继承同一个接口:

[ServiceContract(Namespace = "http://schema.company.com/messages/")]
public interface IBasicContract<TRequest, TResponse>
  where TRequest : class
  where TResponse : class
{
  [OperationContract(Name = "GetReport",
    Action = "http://schema.company.com/messages/GetReport",
    ReplyAction = "http://schema.company.com/messages/GetReportResponse")]
  TResponse GetReport(TRequest inquiry);

  [OperationContract(Name = "GetRawReport",
    Action = "http://schema.company.com/messages/GetRawReport",
    ReplyAction = "http://schema.company.com/messages/GetRawReportResponse")]
  string GetRawReport(string guid);

  [OperationContract(Name = "GetArchiveReport",
    Action = "http://schema.company.com/messages/GetArchiveReport",
    ReplyAction = "http://schema.company.com/messages/GetArchiveReportResponse")]
  TResponse GetArchiveReport(string guid);
}

我还决定创建服务客户端的通用实现:

public class BasicSvcClient<TRequest, TResponse> : ClientBase<IBasicContract<TRequest, TResponse>>, IBasicContract<TRequest, TResponse>
  where TRequest : class
  where TResponse : class
{
  public BasicSvcClient()
  {
  }

  public BasicSvcClient(string endpointConfigurationName) :
    base(endpointConfigurationName)
  {
  }

  public BasicSvcClient(string endpointConfigurationName, string remoteAddress) :
    base(endpointConfigurationName, remoteAddress)
  {
  }

  public BasicSvcClient(string endpointConfigurationName, EndpointAddress remoteAddress) :
    base(endpointConfigurationName, remoteAddress)
  {
  }

  public BasicSvcClient(Binding binding, EndpointAddress remoteAddress) :
    base(binding, remoteAddress)
  {
  }

  public TResponse GetReport(TRequest inquiry)
  {
    return Channel.GetReport(inquiry);
  }

  public string GetRawReport(string guid)
  {
    return Channel.GetRawReport(guid);
  }

  public TResponse GetArchiveReport(string guid)
  {
    return Channel.GetArchiveReport(guid);
  }
}

问题是当我尝试使用这个时:

using (var client = new BasicSvcClient<TRequest, TResponse>())
{
  var response = client.GetReport(inquiry);

  context.Response.ContentType = "text/xml";
  context.Response.Write(response.AsXML());
}

我总是收到一条错误消息,说它找不到合同 IBasicContract 的配置,在 .NET 使用的那种奇怪的语法中:

找不到默认端点 引用合约的元素 'BasicWorkflow.IBasicContract`2...

我试过这样做:

using (var client = new BasicSvcClient<TRequest, TResponse>("myConfig"))

它没有帮助 - 它还在寻找那个特定的合同。

我知道 ServiceContract 属性有一个 ConfigurationName 参数,但我不能在编译时使用它,因为我有 许多 从同一个程序调用的工作流(因此有很多配置条目) .有没有办法在运行时设置 ConfigurationName?我认为这是 ClientBase 构造函数应该做的,但显然不是。

[编辑] 这是 .config 文件中的端点,我不认为它在这种情况下很有帮助:

<endpoint address="https://localhost/services/Contract.svc"
    binding="basicHttpBinding"
    bindingConfiguration="httpsDataEndpoint"
    contract="IContract" name="IContractSvc" />

[Edit2] 好的...我找到了一种可行的方法,尽管我仍然对它不完全满意:

using (var wf = new BasicSvcClient<TRequest, TResponse>(
  new BasicHttpBinding("httpsDataEndpoint"),
  new EndpointAddress("https://localhost/services/Contract.svc")))

我现在唯一的问题是我希望从 .config 文件中检索端点地址(使用实际的合同名称,如 IContract)。有谁能帮我完成这部分吗?

[Edit3] 终于找到了完整的解决方案 :) Reflector 万岁!

var cf = (ClientSection) ConfigurationManager.GetSection("system.serviceModel/client");
foreach (ChannelEndpointElement endpoint in cf.Endpoints)
{
  if (endpoint.Name != "ContractSvc")
    continue;

  using (var wf = new BasicSvcClient<TRequest, TResponse>(
    new BasicHttpBinding("httpsDataEndpoint"),
    new EndpointAddress(endpoint.Address.ToString())))
  {
      //... call wf.GetReport()
  }
  break;
}

【问题讨论】:

    标签: c# wcf generics


    【解决方案1】:

    如果你想使用像 IBasicContract 这样的服务契约,你必须使用在配置文件中定义泛型类型的标准方法

    在配置文件中是这样写的:IBasicContract`2[TRequest,TResponse]

    我也在我的博客 (www.lybecker.com/blog/) 上回复了你的问题。

    :-) 安德斯·莱贝克

    【讨论】:

    • 谢谢 :) 问题在于我从同一个程序中引用了几个不同的工作流程......他们不能都使用同一个合同。无论如何,正如我上面所说,我设法解决了这个问题。再次感谢您抽出宝贵时间回答!
    【解决方案2】:

    为什么不在 ServiceContract 属性中为你的合约指定一个名称:

    [
    ServiceContract
       (
        Namespace = "http://schema.company.com/messages/", 
        Name="MyBasicContract"
        )
    ]
    

    如果您没有明确指定名称,它将默认为“.NET 使用的奇怪语法”中接口的限定名称。

    【讨论】:

    • 问题在于我需要在通用接口上指定名称;这对我没有帮助,因为我有一个调用 20 个工作流的程序,它们都派生自同一个接口。
    【解决方案3】:

    “.NET 使用的奇怪语法”实际上是绑定到特定类型的泛型类型在运行时的类型名称。 Typename`n[[Type],...] 其中 n 表示泛型类型中包含的类型参数的数量。

    您的端点配置如何?

    【讨论】:

    • 我知道泛型类型“mangling”:) 我已将端点添加到问题中。
    • 也许我完全偏离了轨道,但是您的 ClientBase 确实引用了 IBasicContract,而您似乎为 IContract 做了一个定义,我想它是您的 IBasicContract 的继承者?如果您在端点配置中以那种“类型修改”的方式指定合同怎么办?
    • 它可以工作,但看起来很难看 :) contract="BasicWorkflow.IBasicContract`2[[Company.CommonObj.Request, CommonObj, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f459617168919938 ],[Company.CommonObj.Response, CommonObj, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f459617168919938]]"
    • 在哪种情况下我回答了您的问题? ;)
    • 不是真的,我只是用另一种丑陋代替了一种丑陋:P 我会在答案中添加+1,让其他人决定。顺便说一句,我找到了完整的解决方案 - 发布它:)
    猜你喜欢
    • 2011-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2012-03-05
    • 1970-01-01
    相关资源
    最近更新 更多