Flex4中使用WCF
虽然flex跟.net交互的首选是FluorineFx,但是如果在某些特定情况下(比如服务端是现成的,不允许修改,或者服务端开发方不懂FluorineFx为何物),这时webService还是挺有用的。
WebService完全可以用"以BasicHttpBinding方式运行的WCF"代替。经过我的实际测试:对于基本类型(比如int,string,datetime以及它们对应的arrry以list),flex调用时能正确识别并“翻译”成as3中对应的int,String,Date以及Array类型,而复杂类型(比如自己在c#中定义的实体类或DataTable),flex调用时会报错,这类复杂类型我建议在wcf中用序列化技术处理成String后再返回。
考虑到xml格式序列化后的信息量比较大,我倾向于选择json这种轻量级的格式,而且在.net4.0中新增了System.Runtime.Serialization.Json;能处理大多数的复杂对象序列化(但是DataTable处理不了)
为了方便起见,我把一些序列化/反序列化的操作封装了一下:
01 |
using System; |
02 |
using System.IO; |
03 |
using System.Runtime.Serialization.Json; |
04 |
using System.Text; |
05 |
using System.Data; |
06 |
|
07 |
namespace Helper |
08 |
{ |
09 |
public static class Utils |
10 |
{ |
11 |
/// <summary> |
12 |
/// 将对象序列化成json字符串(注:obj的类定义中要加正确的可序列化标志) |
13 |
/// </summary> |
14 |
/// <param name="obj"></param> |
15 |
/// <returns></returns> |
16 |
public static string ToJsonString( object obj) |
17 |
{ |
18 |
string result = string .Empty; |
19 |
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(obj.GetType()); |
20 |
|
21 |
using (MemoryStream ms = new MemoryStream()) |
22 |
{ |
23 |
jsonSerializer.WriteObject(ms, obj); |
24 |
result = Encoding.UTF8.GetString(ms.ToArray()); |
25 |
} |
26 |
|
27 |
return result; |
28 |
} |
29 |
|
30 |
/// <summary> |
31 |
/// json字符串反序列为对象 |
32 |
/// </summary> |
33 |
/// <param name="jsonString"></param> |
34 |
/// <param name="objType"></param> |
35 |
/// <returns></returns> |
36 |
public static Object ToJsonObject( string jsonString, Type objType) |
37 |
{ |
38 |
Object result = null ; |
39 |
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(objType); |
40 |
|
41 |
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString))) |
42 |
{ |
43 |
result = jsonSerializer.ReadObject(ms); |
44 |
} |
45 |
|
46 |
return result; |
47 |
} |
48 |
|
49 |
/// <summary> |
50 |
/// 将DataTable序列化成json字符串 |
51 |
/// </summary> |
52 |
/// <param name="dt"></param> |
53 |
/// <returns></returns> |
54 |
public static string ToJsonString( this DataTable dt) |
55 |
{ |
56 |
|
57 |
StringBuilder JsonString = new StringBuilder(); |
58 |
//Exception Handling |
59 |
if (dt != null && dt.Rows.Count > 0) |
60 |
{ |
61 |
JsonString.Append( "{ " ); |
62 |
JsonString.Append( "\"Rows\":[ " ); |
63 |
for ( int i = 0; i < dt.Rows.Count; i++) |
64 |
{ |
65 |
JsonString.Append( "{ " ); |
66 |
for ( int j = 0; j < dt.Columns.Count; j++) |
67 |
{ |
68 |
if (j < dt.Columns.Count - 1) |
69 |
{ |
70 |
JsonString.Append( "\"" + dt.Columns[j].ColumnName.ToString().Replace( "\"" , "\\\"" ) + "\":" + "\"" + dt.Rows[i][j].ToString().Replace( "\"" , "\\\"" ) + "\"," ); |
71 |
} |
72 |
else if (j == dt.Columns.Count - 1) |
73 |
{ |
74 |
JsonString.Append( "\"" + dt.Columns[j].ColumnName.ToString().Replace( "\"" , "\\\"" ) + "\":" + "\"" + dt.Rows[i][j].ToString().Replace( "\"" , "\\\"" ) + "\"" ); |
75 |
} |
76 |
} |
77 |
/**/ |
78 |
/*end Of String*/ |
79 |
if (i == dt.Rows.Count - 1) |
80 |
{ |
81 |
JsonString.Append( "} " ); |
82 |
} |
83 |
else |
84 |
{ |
85 |
JsonString.Append( "}, " ); |
86 |
} |
87 |
} |
88 |
JsonString.Append( "]}" ); |
89 |
return JsonString.ToString(); |
90 |
} |
91 |
else |
92 |
{ |
93 |
return null ; |
94 |
} |
95 |
} |
96 |
} |
97 |
} |
不过,在开始正文之前,先提醒一下System.Runtime.Serialization.Json在序列化中要注意的问题:
比如有一个类Person,定义如下:
01 |
[Serializable] |
02 |
public class Person |
03 |
{ |
04 |
private string _name; |
05 |
private int _age; |
06 |
private float _salary; |
07 |
08 |
public string Name { set { _name = value; } get { return _name; } } |
09 |
public int Age { set { _age = value; } get { return _age; } } |
10 |
public float Salary { set { _salary = value; } get { return _salary; } } |
11 |
|
12 |
} |
对象
Person p = new Person(){Age=30, Name= "jimmy.yang" , Salary=5000}; |
序列后的字符串为
{"_age":30,"_name":"jimmy.yang","_salary":5000}
注意:这里并不是我所期待的{"Age":30,"Name":"jimmy.yang","Salary":5000},其实出现这样的结果也可以理解,因为属性的set,get内部就是方法调用,因此最终序列化的只是私有字段。但是如果把[Serializable]标志去掉,确能得到正确的结果:{"Age":30,"Name":"jimmy.yang","Salary":5000} 不知道这个算不算是System.Runtime.Serialization.Json的一个bug.
实际flex应用中,用于传输的实体类99%以上保存的只是一些常规的基元类型(即int,string,date之类),所以为了避免上面提到的问题,我建议:
1、实体类定义中只使用基本类型,去掉[Serializable]
2、或者直接把实例成员用类似public string Name;的方式暴露出来,不过估计大数多酷爱OO的同学们要吐血了.
1、实体类定义中只使用基本类型,去掉[Serializable]
2、或者直接把实例成员用类似public string Name;的方式暴露出来,不过估计大数多酷爱OO的同学们要吐血了.
ok,切入正题吧:
1、先创建一个asp.net项目,然后添加一个wcf service,文件命名为:Sample.svc,对应的后端代码文件Sample.svc.cs内容如下:
01 |
using System.Collections.Generic; |
02 |
using System.Data; |
03 |
using System; |
04 |
using Helper; |
05 |
using Entity; |
06 |
|
07 |
namespace WcfApp |
08 |
{ |
09 |
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Sample" in code, svc and config file together. |
10 |
public class Sample : ISample |
11 |
{ |
12 |
|
13 |
|
14 |
public string ObjectTest() |
15 |
{ |
16 |
return Utils.ToJsonString( new Person() { Age=30, Name= "jimmy.yang" , Salary=3000 }); |
17 |
} |
18 |
|
19 |
|
20 |
public string DataTableTest() |
21 |
{ |
22 |
DataTable tbl = new DataTable(); |
23 |
tbl.Columns.Add( "Name" ); |
24 |
tbl.Columns.Add( "Age" ); |
25 |
tbl.Rows.Add( "Jimmy.Yang" , 30); |
26 |
tbl.Rows.Add( "Mike" , 20); |
27 |
return tbl.ToJsonString(); |
28 |
} |
29 |
|
30 |
|
31 |
public string ListObjectTest() |
32 |
{ |
33 |
return Utils.ToJsonString( |
34 |
new List<Person>() { |
35 |
new Person() { Age = 20, Name = "张三" , Salary = 5000 }, |
36 |
new Person() { Age = 30, Name = "李四" , Salary = 8000 } |
37 |
} |
38 |
); |
39 |
} |
40 |
} |
41 |
} |
2、创建flex项目,然后在Data/Services面板中,添加一个webSerivce的引用
![](https://pic002.cnblogs.com/images/2011/27612/2011040911441947.png)
点击Next之后,出现下面的界面
![](https://pic002.cnblogs.com/images/2011/27612/2011040911452372.png)
设置wcf所在的WSDL URI后,一路next,最终Data/WebServices面板会变成下面这样
![](https://pic002.cnblogs.com/images/2011/27612/2011040912193591.png)
注意上图中右侧工具栏上的几个小按钮,自己去试试吧,会有意外发现哦
同时flex会生成几个as类文件(类似于wcf中svcutil.exe在client端自动生成的cs文件)
![](https://pic002.cnblogs.com/images/2011/27612/2011040912220330.png)
剩下的事情,就很容易了,在mxml中测试一番,代码如下:
001 |
<? xml version = "1.0" encoding = "utf-8" ?> |
002 |
< s:Application xmlns:fx = "http://ns.adobe.com/mxml/2009" |
003 |
xmlns:s = "library://ns.adobe.com/flex/spark" |
004 |
xmlns:mx = "library://ns.adobe.com/flex/mx" xmlns:sample = "services.sample.*" > |
005 |
|
006 |
|
007 |
< fx:Script > |
008 |
<![CDATA[ |
009 |
|
010 |
import com.adobe.serialization.json.JSONDecoder; |
011 |
import mx.controls.Alert; |
012 |
import mx.rpc.events.FaultEvent; |
013 |
import mx.rpc.events.ResultEvent; |
014 |
import valueObjects.Person; |
015 |
|
016 |
|
017 |
private function dataTableTest():void |
018 |
{ |
019 |
DataTableTestResult.token = sample.DataTableTest(); |
020 |
DataTableTestResult.addEventListener(ResultEvent.RESULT,onDataTableTestResult); |
021 |
DataTableTestResult.addEventListener(FaultEvent.FAULT,onDataTableTestFault); |
022 |
} |
023 |
|
024 |
private function onDataTableTestFault(e:FaultEvent):void |
025 |
{ |
026 |
Alert.show("dataTableTest调用失败,result="+ e); |
027 |
trace("dataTableTest.Fault=",e); |
028 |
} |
029 |
|
030 |
private function onDataTableTestResult(e:ResultEvent):void |
031 |
{ |
032 |
this.txtDataTable.text = "dataTableTest调用成功,结果:" + e.result; |
033 |
} |
034 |
|
035 |
|
036 |
private function listObjectTest():void |
037 |
{ |
038 |
ListObjectTestResult.token = sample.ListObjectTest(); |
039 |
ListObjectTestResult.addEventListener(ResultEvent.RESULT,onListObjectTestResult); |
040 |
ListObjectTestResult.addEventListener(FaultEvent.FAULT,onListObjectFault); |
041 |
} |
042 |
|
043 |
private function onListObjectTestResult(e:ResultEvent):void |
044 |
{ |
045 |
this.txtList.text = "listObjectTest调用成功,结果:" + e.result; |
046 |
var jsonDecoder:JSONDecoder = new JSONDecoder(e.result.toString(),true); |
047 |
var obj:Object = jsonDecoder.getValue(); |
048 |
//把结果转化为强类型的集合 |
049 |
var list:Vector.<Person> = new Vector.<Person>(); |
050 |
for(var i:int=0;i<obj.length;i++) |
051 |
{ |
052 |
list[i] = new Person(obj[i].Name,obj[i].Age,obj[i].Salary); |
053 |
} |
054 |
trace(list); |
055 |
} |
056 |
|
057 |
private function onListObjectFault(e:FaultEvent):void |
058 |
{ |
059 |
Alert.show("listObjectTest调用失败,result="+ e); |
060 |
trace("listObjectTest.Fault=",e); |
061 |
} |
062 |
|
063 |
private function objectTest():void |
064 |
{ |
065 |
ObjectTestResult.token = sample.ObjectTest(); |
066 |
ObjectTestResult.addEventListener(ResultEvent.RESULT,onObjectTestResult); |
067 |
ObjectTestResult.addEventListener(FaultEvent.FAULT,onObjectTestFault); |
068 |
} |
069 |
|
070 |
private function onObjectTestResult(e:ResultEvent):void |
071 |
{ |
072 |
this.txtObject.text = "objectTest调用成功,返回值:" + e.result; |
073 |
var jsonDecoder:JSONDecoder = new JSONDecoder(e.result.toString(),true); |
074 |
var obj:Object = jsonDecoder.getValue(); |
075 |
var p:Person = new Person(obj.Name,obj.Age,obj.Salary); |
076 |
this.txtObject.text += "\n" + "p.Age=" + p.Age + ",p.Name=" + p.Name + ",p.Salary=" + p.Salary; |
077 |
} |
078 |
|
079 |
private function onObjectTestFault(e:FaultEvent):void |
080 |
{ |
081 |
this.txtObject.text = "objectTest调用失败,原因="+ e; |
082 |
} |
083 |
|
084 |
private function doClick():void |
085 |
{ |
086 |
objectTest(); |
087 |
listObjectTest(); |
088 |
dataTableTest(); |
089 |
} |
090 |
|
091 |
]]> |
092 |
</ fx:Script > |
093 |
< fx:Declarations > |
094 |
< s:CallResponder id = "ArrayTestResult" /> |
095 |
< sample:Sample id = "sample" fault = "Alert.show(event.fault.faultString + '\n' + event.fault.faultDetail)" showBusyCursor = "true" /> |
096 |
< s:CallResponder id = "DataTableTestResult" /> |
097 |
< s:CallResponder id = "ListObjectTestResult" /> |
098 |
< s:CallResponder id = "ObjectTestResult" /> |
099 |
</ fx:Declarations > |
100 |
< s:Panel right = "10" left = "10" bottom = "10" top = "10" title = "WCF 调用实例" > |
101 |
< s:layout > |
102 |
< s:BasicLayout /> |
103 |
</ s:layout > |
104 |
< mx:VDividedBox left = "10" bottom = "40" right = "10" top = "0" > |
105 |
< s:TextArea height = "33%" width = "100%" id = "txtObject" > |
106 |
|
107 |
</ s:TextArea > |
108 |
< s:TextArea height = "33%" width = "100%" id = "txtList" > |
109 |
|
110 |
</ s:TextArea > |
111 |
< s:TextArea height = "33%" width = "100%" id = "txtDataTable" > |
112 |
|
113 |
</ s:TextArea > |
114 |
</ mx:VDividedBox > |
115 |
< s:Button label = "Call Wcf" horizontalCenter = "0" id = "btnCall" click = "doClick()" bottom = "10" /> |
116 |
</ s:Panel > |
117 |
</ s:Application > |
后记:在实际开发中发现flex ide环境对于wcf的wsdl解析要比asmx慢不止N倍,但是一旦解析完成,生成相应的as类后,在运行时二耆速度相同。
注:此文摘自博客园,如有侵权,请速与本人联系,将最快速度删除。