WCF - ServiceContract 继承

来源:互联网 发布:贵州师范学院教务网络 编辑:程序博客网 时间:2024/05/10 13:32

ServiceContract 支持继承的好处是我们可以在不影响现有客户端的情况下自由扩展服务的功能。本文的目的是了解一下继承的一些细节问题。

(注:为了显示方便,以下演示代码中作了些删减。)

演示原型:

服务器端代码

[ServiceContract]
public interface IMyService
{
  [OperationContract]
  int Add(int a, int b);
}

public class MyServie : IMyService
{
  public int Add(int a, int b)
  {
    Console.WriteLine(this.GetHashCode());
    return a + b;
  }
}

public class WcfTest
{
  public static void Test()
  {
    ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService"));
    host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "");

    ServiceMetadataBehavior metadata = new ServiceMetadataBehavior();
    metadata.HttpGetEnabled = true;
    host.Description.Behaviors.Add(metadata);

    host.Open();
  }
}


客户端代码

//------------------------------------------------------------------------------
// <auto-generated>
//   此代码由工具生成。
//   运行库版本:2.0.50727.42
//
//   对此文件的更改可能会导致不正确的行为,并且如果
//   重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------

namespace ConsoleApplication1.localhost
{
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
  [ServiceContractAttribute(ConfigurationName="ConsoleApplication1.localhost.IMyService")]
  public interface IMyService
  {
    [OperationContractAttribute(Action="http://.../IMyService/Add", ReplyAction="...")]
    int Add(int a, int b);
  }
 
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
  public interface IMyServiceChannel : IMyService, IClientChannel
  {
  }
 
  [DebuggerStepThroughAttribute()]
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
  public partial class MyServiceClient : ClientBase<IMyService>, IMyService
  {
    public int Add(int a, int b)
    {
      return base.Channel.Add(a, b);
    }
  }
}


app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IMyService" closeTimeout="00:01:00" ... />
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:8080/MyService" binding="basicHttpBinding"
        bindingConfiguration="BasicHttpBinding_IMyService" contract="IMyService"
        name="BasicHttpBinding_IMyService" />
      </client>
  </system.serviceModel>
</configuration>


接下来,我们对 IMyService 进行升级,我们要创建一个继承接口 IMyService2,以便添加新的方法。自然 MyService要实现的接口也有所变更。由于 "ServiceContractAttribute [AttributeUsage(Inherited =false,...)]",我们同样需要为继承接口添加 "[ServiceContract]"。

[ServiceContract]
public interface IMyService
{
  [OperationContract]
  int Add(int a, int b);
}

[ServiceContract]
public interface IMyService2 : IMyService
{
  [OperationContract]
  void Test();
}

public class MyServie : IMyService2
{
  public int Add(int a, int b)
  {
    Console.WriteLine(this.GetHashCode());
    return a + b;
  }

  public void Test()
  {
    Console.WriteLine("Test...");
  }
}

public class WcfTest
{
  public static void Test()
  {
    ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService"));
    host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "");
    //host.AddServiceEndpoint(typeof(IMyService2), new BasicHttpBinding(), "v2");

    ServiceMetadataBehavior metadata = new ServiceMetadataBehavior();
    metadata.HttpGetEnabled = true;
    host.Description.Behaviors.Add(metadata);

    host.Open();
  }
}


如果我们没有为 IMyService2 添加一个新的 Endpoint,一切都还是老样子,重新创建的客户端代码和配置文件与最初没什么区别,仿佛IMyservice2 未曾出现过。接下来我们继续,将上面代码中的注释符号去掉,我们为 IMyService2 增加了一个新的Endpoint,重新创建的客户端代码会是什么样子呢?创建客户端时,我们依然使用 "http://localhost:8080/MyService" 而不是 "http://localhost:8080/MyService/v2"。

//------------------------------------------------------------------------------
// <auto-generated>
//   此代码由工具生成。
//   运行库版本:2.0.50727.42
//
//   对此文件的更改可能会导致不正确的行为,并且如果
//   重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------

namespace ConsoleApplication1.localhost
{
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
  [ServiceContractAttribute(ConfigurationName="ConsoleApplication1.localhost.IMyService")]
  public interface IMyService
  {
    [OperationContractAttribute(Action="http://.../IMyService/Add", ReplyAction="...")]
    int Add(int a, int b);
  }
 
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
  public interface IMyServiceChannel : IMyService, IClientChannel
  {
  }
 
  [DebuggerStepThroughAttribute()]
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
  public partial class MyServiceClient : ClientBase<IMyService>, IMyService
  {
    public int Add(int a, int b)
    {
      return base.Channel.Add(a, b);
    }
  }
 
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
  [ServiceContractAttribute(ConfigurationName="ConsoleApplication1.localhost.IMyService2")]
  public interface IMyService2
  {
    [OperationContractAttribute(Action="http://.../IMyService/Add", ReplyAction="...")]
    int Add(int a, int b);
 
    [OperationContractAttribute(Action="http://.../IMyService2/Test", ReplyAction="...")]
     void Test();
  }
 
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
  public interface IMyService2Channel : IMyService2, IClientChannel
  {
  }
 
  [DebuggerStepThroughAttribute()]
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
  public partial class MyService2Client : ClientBase<IMyService2>, IMyService2
  {
    public int Add(int a, int b)
    {
      return base.Channel.Add(a, b);
    }
 
    public void Test()
    {
      base.Channel.Test();
    }
  }
}


app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IMyService" closeTimeout="00:01:00" ... />
        <binding name="BasicHttpBinding_IMyService2" closeTimeout="00:01:00" ... />
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:8080/MyService" binding="basicHttpBinding"
        bindingConfiguration="BasicHttpBinding_IMyService" contract="IMyService"
        name="BasicHttpBinding_IMyService" />

      <endpoint address="http://localhost:8080/MyService/v2" binding="basicHttpBinding"
        bindingConfiguration="BasicHttpBinding_IMyService2" contract="IMyService2"
        name="BasicHttpBinding_IMyService2" />
    </client>
  </system.serviceModel>
</configuration>


看到了什么?没错,在原有的基础上增加了完整的 IMyService2代理实现。这也就意味着原有客户端只需更新自动生成的代码就可以使用新的服务方法,当然不做调整的客户端依旧可以正常运行。但是有一点要注意,客户端生成的新老接口和代理类型之间并没有继承关系,所以在客户端我们并不能使用 "多态" 代理。

要是我们使用 "http://localhost:8080/MyService/v2" 来创建客户端,会是什么结果?结果就是和上面的一样,也会自动生成新老接口和代理。在使用 "http://localhost:8080/MyService/v2" 创建客户端代码前,记得调整 ServiceMetadataBehavior 的地址。

ServiceHost host = new ServiceHost(typeof(MyServie), new Uri("http://localhost:8080/MyService"));
host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "");
host.AddServiceEndpoint(typeof(IMyService2), new BasicHttpBinding(), "v2");

ServiceMetadataBehavior metadata = new ServiceMetadataBehavior();
metadata.HttpGetUrl = new Uri("http://localhost:8080/MyService/v2");
metadata.HttpGetEnabled = true;
host.Description.Behaviors.Add(metadata);

host.Open();


直接使用 "Update Services Reference" 功能时,会出现如下异常。不知道是 Bug,还是我的系统有问题?

uploads/200704/12_122918_wcf1.gif

 

uploads/200704/12_122922_wcf2.gif
原创粉丝点击