虽然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处理不了)
为了方便起见,我把一些序列化/反序列化的操作封装了一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
using
System;
using
System.IO;
using
System.Runtime.Serialization.Json;
using
System.Text;
using
System.Data;
namespace
Helper
{
public
static
class
Utils
{
/// <summary>
/// 将对象序列化成json字符串(注:obj的类定义中要加正确的可序列化标志)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public
static
string
ToJsonString(
object
obj)
{
string
result =
string
.Empty;
DataContractJsonSerializer jsonSerializer =
new
DataContractJsonSerializer(obj.GetType());
using
(MemoryStream ms =
new
MemoryStream())
{
jsonSerializer.WriteObject(ms, obj);
result = Encoding.UTF8.GetString(ms.ToArray());
}
return
result;
}
/// <summary>
/// json字符串反序列为对象
/// </summary>
/// <param name="jsonString"></param>
/// <param name="objType"></param>
/// <returns></returns>
public
static
Object ToJsonObject(
string
jsonString, Type objType)
{
Object result =
null
;
DataContractJsonSerializer jsonSerializer =
new
DataContractJsonSerializer(objType);
using
(MemoryStream ms =
new
MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
{
result = jsonSerializer.ReadObject(ms);
}
return
result;
}
/// <summary>
/// 将DataTable序列化成json字符串
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public
static
string
ToJsonString(
this
DataTable dt)
{
StringBuilder JsonString =
new
StringBuilder();
//Exception Handling
if
(dt !=
null
&& dt.Rows.Count > 0)
{
JsonString.Append(
"{ "
);
JsonString.Append(
"\"Rows\":[ "
);
for
(
int
i = 0; i < dt.Rows.Count; i++)
{
JsonString.Append(
"{ "
);
for
(
int
j = 0; j < dt.Columns.Count; j++)
{
if
(j < dt.Columns.Count - 1)
{
JsonString.Append(
"\""
+ dt.Columns[j].ColumnName.ToString().Replace(
"\""
,
"\\\""
) +
"\":"
+
"\""
+ dt.Rows[i][j].ToString().Replace(
"\""
,
"\\\""
) +
"\","
);
}
else
if
(j == dt.Columns.Count - 1)
{
JsonString.Append(
"\""
+ dt.Columns[j].ColumnName.ToString().Replace(
"\""
,
"\\\""
) +
"\":"
+
"\""
+ dt.Rows[i][j].ToString().Replace(
"\""
,
"\\\""
) +
"\""
);
}
}
/**/
/*end Of String*/
if
(i == dt.Rows.Count - 1)
{
JsonString.Append(
"} "
);
}
else
{
JsonString.Append(
"}, "
);
}
}
JsonString.Append(
"]}"
);
return
JsonString.ToString();
}
else
{
return
null
;
}
}
}
}
|
不过,在开始正文之前,先提醒一下System.Runtime.Serialization.Json在序列化中要注意的问题:
比如有一个类Person,定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
[Serializable]
public
class
Person
{
private
string
_name;
private
int
_age;
private
float
_salary;
public
string
Name {
set
{ _name = value; }
get
{
return
_name; } }
public
int
Age {
set
{ _age = value; }
get
{
return
_age; } }
public
float
Salary {
set
{ _salary = value; }
get
{
return
_salary; } }
}
|
对象
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内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
using
System.Collections.Generic;
using
System.Data;
using
System;
using
Helper;
using
Entity;
namespace
WcfApp
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Sample" in code, svc and config file together.
public
class
Sample : ISample
{
public
string
ObjectTest()
{
return
Utils.ToJsonString(
new
Person() { Age=30, Name=
"jimmy.yang"
, Salary=3000 });
}
public
string
DataTableTest()
{
DataTable tbl =
new
DataTable();
tbl.Columns.Add(
"Name"
);
tbl.Columns.Add(
"Age"
);
tbl.Rows.Add(
"Jimmy.Yang"
, 30);
tbl.Rows.Add(
"Mike"
, 20);
return
tbl.ToJsonString();
}
public
string
ListObjectTest()
{
return
Utils.ToJsonString(
new
List<Person>() {
new
Person() { Age = 20, Name =
"张三"
, Salary = 5000 },
new
Person() { Age = 30, Name =
"李四"
, Salary = 8000 }
}
);
}
}
}
|
2、创建flex项目,然后在Data/Services面板中,添加一个webSerivce的引用
![](https://i-blog.csdnimg.cn/blog_migrate/657a3c836105add8c81c7c85e2e44e00.png)
点击Next之后,出现下面的界面
![](https://i-blog.csdnimg.cn/blog_migrate/1d226409d6b571274f9b34359c655a8f.png)
设置wcf所在的WSDL URI后,一路next,最终Data/WebServices面板会变成下面这样
![](https://i-blog.csdnimg.cn/blog_migrate/e3ed15736884fe936174a7ee9a7fce45.png)
注意上图中右侧工具栏上的几个小按钮,自己去试试吧,会有意外发现哦
同时flex会生成几个as类文件(类似于wcf中svcutil.exe在client端自动生成的cs文件)
![](https://i-blog.csdnimg.cn/blog_migrate/39f2ac0012315b36ea5e96c9e2578882.png)
剩下的事情,就很容易了,在mxml中测试一番,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:sample="services.sample.*">
<fx:Script>
<![CDATA[
import com.adobe.serialization.json.JSONDecoder;
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import valueObjects.Person;
private function dataTableTest():void
{
DataTableTestResult.token = sample.DataTableTest();
DataTableTestResult.addEventListener(ResultEvent.RESULT,onDataTableTestResult);
DataTableTestResult.addEventListener(FaultEvent.FAULT,onDataTableTestFault);
}
private function onDataTableTestFault(e:FaultEvent):void
{
Alert.show("dataTableTest调用失败,result="+ e);
trace("dataTableTest.Fault=",e);
}
private function onDataTableTestResult(e:ResultEvent):void
{
this.txtDataTable.text = "dataTableTest调用成功,结果:" + e.result;
}
private function listObjectTest():void
{
ListObjectTestResult.token = sample.ListObjectTest();
ListObjectTestResult.addEventListener(ResultEvent.RESULT,onListObjectTestResult);
ListObjectTestResult.addEventListener(FaultEvent.FAULT,onListObjectFault);
}
private function onListObjectTestResult(e:ResultEvent):void
{
this.txtList.text = "listObjectTest调用成功,结果:" + e.result;
var jsonDecoder:JSONDecoder = new JSONDecoder(e.result.toString(),true);
var obj:Object = jsonDecoder.getValue();
//把结果转化为强类型的集合
var list:Vector.<Person> = new Vector.<Person>();
for(var i:int=0;i<obj.length;i++)
{
list[i] = new Person(obj[i].Name,obj[i].Age,obj[i].Salary);
}
trace(list);
}
private function onListObjectFault(e:FaultEvent):void
{
Alert.show("listObjectTest调用失败,result="+ e);
trace("listObjectTest.Fault=",e);
}
private function objectTest():void
{
ObjectTestResult.token = sample.ObjectTest();
ObjectTestResult.addEventListener(ResultEvent.RESULT,onObjectTestResult);
ObjectTestResult.addEventListener(FaultEvent.FAULT,onObjectTestFault);
}
private function onObjectTestResult(e:ResultEvent):void
{
this.txtObject.text = "objectTest调用成功,返回值:" + e.result;
var jsonDecoder:JSONDecoder = new JSONDecoder(e.result.toString(),true);
var obj:Object = jsonDecoder.getValue();
var p:Person = new Person(obj.Name,obj.Age,obj.Salary);
this.txtObject.text += "\n" + "p.Age=" + p.Age + ",p.Name=" + p.Name + ",p.Salary=" + p.Salary;
}
private function onObjectTestFault(e:FaultEvent):void
{
this.txtObject.text = "objectTest调用失败,原因="+ e;
}
private function doClick():void
{
objectTest();
listObjectTest();
dataTableTest();
}
]]>
</fx:Script>
<fx:Declarations>
<s:CallResponder id="ArrayTestResult"/>
<sample:Sample id="sample" fault="Alert.show(event.fault.faultString + '\n' + event.fault.faultDetail)" showBusyCursor="true"/>
<s:CallResponder id="DataTableTestResult"/>
<s:CallResponder id="ListObjectTestResult"/>
<s:CallResponder id="ObjectTestResult"/>
</fx:Declarations>
<s:Panel right="10" left="10" bottom="10" top="10" title="WCF 调用实例">
<s:layout>
<s:BasicLayout/>
</s:layout>
<mx:VDividedBox left="10" bottom="40" right="10" top="0">
<s:TextArea height="33%" width="100%" id="txtObject">
</s:TextArea>
<s:TextArea height="33%" width="100%" id="txtList">
</s:TextArea>
<s:TextArea height="33%" width="100%" id="txtDataTable">
</s:TextArea>
</mx:VDividedBox>
<s:Button label="Call Wcf" horizontalCenter="0" id="btnCall" click="doClick()" bottom="10"/>
</s:Panel>
</s:Application>
后记:在实际开发中发现flex ide环境对于wcf的wsdl解析要比asmx慢不止N倍,但是一旦解析完成,生成相应的as类后,在运行时二耆速度相同。