复习WCF(2)----- 创建服务和数据协定

 

服务协定:使用ServiceContractAttribute属性修饰

服务方法:使用OperationContractAttribute属性修饰

服务协定可以用接口实现 也可以用普通的类来实现 推荐使用接口

至于接口的好处或者接口是什么 连接:http://www.cnblogs.com/Scissors/articles/2922885.html

如果是使用接口实现 那实现此服务协定(接口)的类就不用添加ServiceContract和OperationContract属性来修饰。

具体代码如下所示:

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetData(int value);
    }
    public class Service1 : IService1
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }
    }

注意:

1.服务中的方法就算返回值为void ,它并不是真的就不反回任何值了,它还是会返回一个值  这个在后面会讲到。

2.还有就是,像以前的局部方法里需要一个某类型的参数,你直接在调用的时候就把参数放上去,实际上是传的对象的引用

可是在wcf里不是这样,因为wcf都是跨internet的,别说不在同一台电脑,甚至都可能不在同一个国家。所以

它传的是对象的副本,这一点需要强烈认知,因为既然是传的对象的副本,那就要求参数或返回值的类型都是可以序列化的,

也就是说 ,该类型的对象可以转换为字节流,又能从字节流转换为对象。当然,.net framework里面的大部分类型都能序列化

 

WCF的3种模式

1.请求/答复模式 (默认模式)

[OperationContract]
string GetData(int value);

当调用wcf方法时(请求),会一直等待,知道接收到返回的消息(答复、响应),才执行下一步操作。

缺点:如果wcf方法中执行的操作需要很长时间,则会降低客户端性能和响应能力。

优点:因为可返回 或者说是可以响应客户端,调用中难免发生SOAP错误,而消息中可以返回这些错误。

 

2.单向模式

[OperationContract(IsOneWay=true)]
string GetData(int value);

顾名思义,单向,就是客户端调用完之后我不管你服务端执行得怎么样了,你有没有返回值我无所谓,我只要负责把请求送到你那就OK了。

单向模式有点类似于事件,当某段代码里激发事件后,其实大家都不知道事件到底是在哪一步或者哪一时间发生的,什么时候又执行到哪一步了。

单向操作是客户端调用操作并在wcf将消息写入网络后继续进行自己处理的操作,通常意味着,除非发出消息中的数据很庞大需要等一会儿,否则客户端几乎是立即继续执行

自己的操作(除非发送数据时就出错了)。

如果客户端不需要等待这个wcf服务操作完成,而且也不需要处理可能会发生的soap错误  那么请将返回void的操作使用单向模式比较好。

 

3.双工模式 (会要复杂点)

我看的教程的原话:双工模式的特点是,无论使用单向消息发送还是请求/ 答复消息发送方式,服务和客户端均能够独立地向对方发送消息。对于必须

直接与客户端通信或向消息交换的任意一方提供异步体验(包括 接与客户端通信或向消息交换的任意 方提供异步体验 包括类似于事件的行为)的服务来说,

这种双向通信形式非常有用

用我的话来说:双工模式就是除了普通的操作之外(无论是单向还是请求与答复),服务端还有一次调用客户端方法的机会

 

具体代码演示:因为是服务端要调用客户端,服务端又怎么知道调用哪一个呢?所以得先对服务端建模,也就是定义一个回调的接口

WCF服务协定:

    //SessionMode.Required  因为这是一系列的加减乘除的操作,所以需要一个会话
    //CallbackContract指定客户端回调的接口
    [ServiceContract(SessionMode = SessionMode.Required, 
        CallbackContract=typeof(ICalculatorDuplexCallBack))]
    public interface ICalculatorDuplex
    {
        [OperationContract(IsOneWay = true)]
        void Clear();
        [OperationContract(IsOneWay = true)]
        void AddTo(double n);
        [OperationContract(IsOneWay = true)]
        void SubtractFrom(double n);
        [OperationContract(IsOneWay = true)]
        void MultiplyBy(double n);
        [OperationContract(IsOneWay = true)]
        void DivideBy(double n);
    }

    public interface ICalculatorDuplexCallBack
    {
        [OperationContract(IsOneWay = true)]
        void Equals(double result);
        [OperationContract(IsOneWay = true)]
        void Equation(string eqn);
    }

因为ICalculatorDuplexCallBack是要交给客户端去实现,并不是客户端需要调用的协定,所以不需要ServiceContract属性来修饰

但是方法上需要OperationContract来修饰

实现协定:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] //每一个会话创建一个实例  保证前后实例相同
    public class CalculatorService : ICalculatorDuplex 
    {
        double result;
        string equation;
        ICalculatorDuplexCallBack callback = null;

        public CalculatorService()
        {
            result = 0.0D;
            equation = result.ToString();
            callback = OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallBack>();
        }

        public void Clear()
        {
            callback.Equation(equation + " = " + result.ToString());
            result = 0.0D;
            equation = result.ToString();
        }

        public void AddTo(double n)
        {
            result += n;
            equation += " + " + n.ToString();
            callback.Equals(result);
        }

        public void SubtractFrom(double n)
        {
            result -= n;
            equation += " - " + n.ToString();
            callback.Equals(result);
        }

        public void MultiplyBy(double n)
        {
            result *= n;
            equation += " * " + n.ToString();
            callback.Equals(result);
        }

        public void DivideBy(double n)
        {
            result /= n;
            equation += " / " + n.ToString();
            callback.Equals(result);
        }
    }

配置文件:因为要支持回调 请将绑定方式更改为wsDualHttpBinding

      <service name="WcfServiceLibrary1.CalculatorService">
        <endpoint address="" binding="wsDualHttpBinding" contract="WcfServiceLibrary1.ICalculatorDuplex">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/CalculatorService/" />
          </baseAddresses>
        </host>
      </service>

至此代码已经写完,可以看到只是普通的加减乘除,而且都是单向模式,我们现在并不知道Equals和Equation到底是做什么的

(吐槽一下  尼玛写博文比写教学PPT累多了!!!)

 

然后实现客户端 客户端用控制台应用程序实现

客户端回调类:

    //实现回调接口
    public class CallbackHandler : ServiceReference1.ICalculatorDuplexCallback 
    {

        public void Equals(double result)
        {
            Console.WriteLine("Result ({0})", result);
        }

        public void Equation(string eqn)
        {
            Console.WriteLine("Equation ({0})", eqn);
        }
    }

初学者肯定很好奇在客户端怎么实现(继承)的ICalculatorDuplexCallback 其实当你添加服务引用后 会在本地自动生成一个代理类 就在那里面

关键调用代码:

class Program
    {
        static void Main(string[] args)
        {
            //这个不知道怎么讲啊 大概就是实例化我们回调的类传给我们双工的服务
            System.ServiceModel.InstanceContext instanceContext = new InstanceContext(new CallbackHandler());
            ServiceReference1.CalculatorDuplexClient client = new ServiceReference1.CalculatorDuplexClient(instanceContext);

            double value = 100.00D;
            client.AddTo(value);

            value = 50.00D;
            client.SubtractFrom(value);

            value = 17.65D;
            client.MultiplyBy(value);

            value = 2.00D;
            client.DivideBy(value);

            client.Clear();
            Console.ReadLine();
            client.Close();
        }
    }

从上至下的看过来 是不是有点点明白了

运行程序后的样子:

相信大家应该能够理解了吧

调用的服务完全没有输出的代码,并且在WCF里写上Console.WriteLine也是没有用的 完全都是靠那个回调接口

本来想把demo上传上来  又找不到地方上传  蛋疼了

 

 

 

数据协定

用DataContractAttribute和DataMemberAttribute修饰

   [DataContract]
    public class Entity
    {
        [DataMember]
        public string PropertyOne
        {
            get;
            set;
        }

        [DataMember]
        public string PropertyTwo
        {
            get;
            set;
        }
    }

这样一来,这个Entity做为一个数据载体(实体类),就可以生成在你客户端的代理类里面

如果不是.Net平台是其他的 比如J2EE啥,因为他是可序列化的,也可以用其他平台的方法反序列化回来使用

代码演示  数据协定:

    [DataContract]
    public class CompositeType
    {
        bool boolValue = true;
        string stringValue = "Hello ";

        [DataMember]
        public bool BoolValue
        {
            get { return boolValue; }
            set { boolValue = value; }
        }

        [DataMember]
        public string StringValue
        {
            get { return stringValue; }
            set { stringValue = value; }
        }
    }

服务协定使用数据协定:

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);
    }
    public class Service1 : IService1
    {public CompositeType GetDataUsingDataContract(CompositeType composite)
        {
            if (composite == null)
            {
                throw new ArgumentNullException("composite");
            }
            if (composite.BoolValue)
            {
                composite.StringValue += "Suffix";
            }
            return composite;
        }

    }

控制台程序测试:

class Program
    {
        static void Main(string[] args)
        {//*****************************数据协定测试******************************
            ServiceReference2.Service1Client client = new ServiceReference2.Service1Client();
            ServiceReference2.CompositeType entity = new ServiceReference2.CompositeType();
            ServiceReference2.CompositeType entity2 = null;

            entity.BoolValue = true;
            entity.StringValue = "周健韬";
            entity2 = client.GetDataUsingDataContract(entity);

            Console.Write(entity2.BoolValue + "   -----   " + entity2.StringValue);
            Console.Read();
            //*****************************数据协定测试******************************
        }
    }

结果:

 

 

 

关于out和ref参数

如果在服务上的参数使用了out或ref  那这个服务就必然是请求/答复模式

因为就算此服务返回值为void,out和ref的作用还是会将要把值改变 

代码演示:

   [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetData(int value);

        [OperationContract]
        string GetData2(int value, ref string strRef, out string strOut); 
    }
    public class Service1 : IService1
    {public string GetData2(int value, ref string strRef, out string strOut)
        {
            strRef += "---- changed by wcf method";

            strOut = "from wcf method";

            return string.Format("you entered ({0})", value);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {//*****************************out和ref测试*****************************
            ServiceReference2.Service1Client client = new ServiceReference2.Service1Client();

            string strRef = "周健韬";
            string strOut;
            client.GetData2(1, ref strRef, out strOut);
            Console.Write(strRef + " ----  " + strOut);
            Console.Read();
            //*****************************out和ref测试*****************************
        }
    }

 要注意一下  服务中不允许两个同名的方法,就算是重载的也不行

posted @ 2013-02-19 14:08  韬韬韬你羞得无礼  Views(230)  Comments(0)    收藏  举报