WCF-异步调用和两种客户端形式

    当发布一个服务端之后,客户端可以通过服务端的元数据,用VS2010添加服务引用的方式生成对应的代码。并且可以选择生成相应的异步操作。

WCF实现代码,Add操作延时5秒后再返回结果。

[ServiceContract]
    public interface ICalculator
    {
        [OperationContract]
        int Add(int x, int y);
    }

    [ServiceBehavior]
    public class Cal : ICalculator
    {
        public int Add(int x, int y)
        {
            System.Threading.Thread.Sleep(5000);
            return x + y;
        }
    }

 服务寄宿:

 static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(typeof(kk.Cal));
            ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
            behavior.HttpGetUrl = new Uri("http://localhost:6666/meta");
            behavior.HttpGetEnabled = true;
            host.Description.Behaviors.Add(behavior);
            host.AddServiceEndpoint(typeof(kk.ICalculator), new WSHttpBinding(), "http://localhost:6666");
            host.Opened+=delegate{Console.WriteLine("Service start!");};
            host.Open();
            Console.ReadLine();
        }

 通过ServiceMetadataBehavior的HttpGetUrl可以设置服务的元数据地址,客户端通过这个地址获取到服务的全部信息。

先运行服务端。再按下列步骤在客户端添加服务引用

1.添加服务引用

2.输入服务公布的元数据地址。

3.在高级中选中生成异步操作。再点击2中的前往即可。

一、通过BeginAdd/EndBegin方法调用客户端。

生成成功后通过默认命名空间ServiceReference1获取到客户端,直接调用BeginAdd方法进行异步调用。

static void Main(string[] args)
        {
             ServiceReference1.CalculatorClient client = new ServiceReference1.CalculatorClient();
            client.BeginAdd(1, 2, CallBack, client);
            for (int i = 0; i < 100; i++)
            {
                System.Threading.Thread.Sleep(1000);
                Console.WriteLine("{0}", i);
            }
        }

        public static void CallBack(IAsyncResult ar)
        {
            ServiceReference1.CalculatorClient client = ar.AsyncState as ServiceReference1.CalculatorClient;
            int result = client.EndAdd(ar);
            Console.WriteLine("Result:{0}", result);
        }

BeginAdd的前两个参数是契约接口参数。第三个是回调方法,参数类型为IAsyncResult返回值为void的委托。第四个是Object类,这里将client客户端传进去,因为需要在回调方法中调用EndAdd方法得到结果。或者直接把client定义成static成员变量,这样就不需要传入了。

二、通过xxxCompleted添加委托。

static void Main(string[] args)
        {
             ServiceReference1.CalculatorClient client = new ServiceReference1.CalculatorClient();
             client.AddCompleted += delegate(object sender, ServiceReference1.AddCompletedEventArgs e)
             {
               int[] para =   e.UserState as int[];
               int result = e.Result;
               Console.WriteLine("Result:{0}+{1}={2}", para[0], para[1], result);
             };
            client.AddAsync(1, 2,new int[]{1,2});
            for (int i = 0; i < 100; i++)
            {
                System.Threading.Thread.Sleep(1000);
                Console.WriteLine("{0}", i);
            }
        }

 Completed+=委托是上面的一种变形,直接通过completedEventArgs的Result属性获取结果,UserState获取附加参数。

上面两种异步调用实现效果为:

通过观察在客户端通过添加服务引用生成的代码,

public partial class CalculatorClient : System.ServiceModel.ClientBase<client2.ServiceReference1.ICalculator>, client2.ServiceReference1.ICalculator

发现ServiceReference1.CalculatorClient是继承自

public abstract class ClientBase<TChannel> : ICommunicationObject, IDisposable where TChannel : class

这也意味着我们可以自定义一个Cinent,而不需要去生成而产生很多无用的代码。

using System.ServiceModel;
using System.ServiceModel.Channels;
namespace client2
{
    class Program
    {
        static void Main(string[] args)
        {
            myClient client = new myClient(new WSHttpBinding(), new EndpointAddress(new Uri("http://localhost:6666")));
            int result=client.myChannel.Add(1, 2);
            Console.WriteLine("{0}", result);
        }
    }

    public class myClient : ClientBase<kk.ICalculator>
    {
        public myClient(Binding bind, EndpointAddress addr)
            : base(bind, addr)
        { 
        }
        public kk.ICalculator myChannel
        {
            get 
            {
                return this.Channel;
            }
        }
    }
}

 myClient继承ClientBase并指定泛型契约,重写基类构造函数,指定Binding和EndpointAddress。并通过返回基类Channel属性。构造的时候传入服务端使用的绑定模式,和终结点地址。然后通过myChannel即可获取通道。其实这个ClientBase内部也是通过ChannelFactory<T>实现的,通过信道工厂创建信道,通过信道调用服务端方法。下面代码是通过信道工厂实现。

static void Main(string[] args)
        {
            ChannelFactory<kk.ICalculator> factory = new ChannelFactory<kk.ICalculator>(new WSHttpBinding(), new EndpointAddress(new Uri("http://localhost:6666")));
            kk.ICalculator mychannel=factory.CreateChannel();
            mychannel.Add(1, 2);
        }
原文地址:https://www.cnblogs.com/lh218/p/4405251.html