复习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测试*****************************
}
}

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

浙公网安备 33010602011771号