Service contract

定义和实现服务协议。

[ServiceContract]
public interface ICalcService
{
    [OperationContract]
    void Add();


    void Sub();

}

public class CalcService : ICalcService
{

    public void Add()
    {
        Console.WriteLine("add");
    }

    public void Sub()
    {
        Console.WriteLine("sub");
    }
}

如你所见,只有Add()上面添加了属性OperationContract。这是如果你在客户端添加服务引用,则只有Add()对于客户端可见,而未添加对应属性的不可见。

(ServiceContract也是同样的道理。不过未添加,服务都无法启动。)

ServiceContract Attribute

(此处我们主要关心 Namespace、Name 这两个,其他的忽略暂时)

  • ServiceContractAttribute.cs
// 摘要: 
//     指示接口或类在 Windows Communication Foundation (WCF) 应用程序中定义服务协定。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class ServiceContractAttribute : Attribute
{
    // 摘要: 
    //     初始化 System.ServiceModel.ServiceContractAttribute 类的新实例。
    public ServiceContractAttribute();

    // ... 
    
    //
    // 摘要: 
    //     获取或设置 Web 服务描述语言 (WSDL) 中的 <portType> 元素的名称。
    //
    // 返回结果: 
    //     默认值为应用了 System.ServiceModel.ServiceContractAttribute 的类或接口的名称。
    //
    // 异常: 
    //   System.ArgumentNullException:
    //     该值为 null。
    //
    //   System.ArgumentOutOfRangeException:
    //     该值是一个空字符串。
    public string Name { get; set; }
    
    //
    // 摘要: 
    //     获取或设置 Web 服务描述语言 (WSDL) 中的 <portType> 元素的命名空间。
    //
    // 返回结果: 
    //     <portType> 元素的 WSDL 命名空间。 默认值为“http://tempuri.org”。
    public string Namespace { get; set; }
    
    //...
}
  • Namespace

用于设置服务的命名空间,由于避免放到互联网中与其它服务冲突,命名空间必须是唯一的(其实就是XML命名空间), 默认是http://tempuri.org/

  • Name

可以为服务起一个别名(昵称)。便于调用或者业务的理解。

上文提及的WSDL,你也可以浏览器中自己查看。

Extension

WSDL

client如何知道server提供的功能清单

Multi Contract

WCF服务端是先定义服务协定,其实就是一个接口,然后通过实现接口来定义服务类。那么,有一个问题,如果一个服务类同时实现N个接口(也就是有N个协定)呢?结果会如何?

(此处不讨论多个服务。多个服务就是独立的服务引用。互不干涉)

一、 Server Define contracts

[ServiceContract]
public interface IServiceOne
{
    [OperationContract]
    void sayOne();
}

[ServiceContract]
public interface IServiceTwo
{
    [OperationContract]
    void sayTwo();
}

[ServiceContract]
public interface IServiceThree
{
    [OperationContract]
    void sayThree();
}

二、 Server Implements contracts

public class MultiService : IServiceOne, IServiceTwo, IServiceThree
{
    public void sayOne()
    {
        Console.WriteLine("say one");
    }

    public void sayTwo()
    {
        Console.WriteLine("say two");
    }

    public void sayThree()
    {
        Console.WriteLine("say three");
    }
}

三、 Server Main()

我们仅仅让服务类实现多个协定的接口是不够的,还要把希望对客户端公开的协定添加为终结点,对,一个协定一个终结点,不添加终结点的协定就不公开。

static void Main(string[] args)
{
    // 基址URI,必须,HTTP方案  
    Uri baseURI = new Uri("http://localhost:8008/Service");

    using (ServiceHost host = new ServiceHost(typeof(MultiService), baseURI))
    {
        // 向服务器添终结点  
        WSHttpBinding binding = new WSHttpBinding();
        
        // 这里不需要安全验证  
        binding.Security.Mode = SecurityMode.None;

        // 添加对应的协议。需要添加多个!
        host.AddServiceEndpoint(typeof(IServiceOne), binding, "my1");
        host.AddServiceEndpoint(typeof(IServiceTwo), binding, "my2");
        host.AddServiceEndpoint(typeof(IServiceThree), binding, "my3");


        // 为了能让VS生成客户端代码,即WSDL文档,故要添加以下行为  
        ServiceMetadataBehavior mdBehavior = new ServiceMetadataBehavior()
        {
            HttpGetEnabled = true
        };
        host.Description.Behaviors.Add(mdBehavior);

        //如果服务顺利启动,则提示,处理Opened事件  
        host.Opened += (sender, e) => Console.WriteLine("服务已启动。");
        // 启动服务器  
        try
        {
            host.Open();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

        // 为了让程序不往下执行而结束,故加上这句  
        Console.ReadKey();
        // 关闭服务器  
        host.Close();
    }  
}

四、Client

每一个协议都会生成一个对应的client。所以客户端调用代码如下:

static void Main(string[] args)
{
    MultiService.ServiceOneClient oneClient = new MultiService.ServiceOneClient();
    oneClient.sayOne();

    MultiService.ServiceTwoClient twoClient = new MultiService.ServiceTwoClient();
    twoClient.sayTwo();

    MultiService.ServiceThreeClient threeClient = new MultiService.ServiceThreeClient();
    threeClient.sayThree();
}