您的位置:首页 > 其它

转 silverlight获取外部数据的另一种选择:FluorineFx

2010-07-27 17:19 357 查看
Silverlight从其它系统获取外部数据的常规途径无非下面2种:
1、直接远程加载文本或xml文件(直接请求ashx/aspx,然后在ashx/aspx上输出信息也可以归入这一类)

2、通过wcf/webService取得数据

(当然,sl跟本机的sl之间也能交换数据,但这个用处有限,此外通过socket也能拿到数据,但是socket要玩好并不容易,难度系数有点高,本文不做讨论)

而返回的数据格式,最常用的通常为"xml"、"json字符串"(或普通字符串)或"最原始的Stream"

今天在学习FluorineFx(一个开源的免费项目),并查看它的演示示例时,意外发现FluorineFx也支持silverlight!

与webService采用的soap协议不同:FluorineFx支持Adobe的AMF0,AMF3,RTMP协议,能方便的与Adobe几乎所有的通讯技术交互,这为silverlight与flash/flex交互提供了可能。(详见百度百科FluorineFXhttp://baike.baidu.com/view/1654458.htm?fr=ala0_1)

下面简单说下silverlight中使用FluorineFx的大概步骤:

基本上silverlight本身只是一种UI技术,自身并无太强的的数据处理能力(独立存储虽然提供了数据存储和检索能力,但能力实在有限),要获取数据只能借助其它系统或技术,所以我们先把其它系统做好:

1、先用VS.Net(我用的是vs2010)创建一个Library项目,起名为ServiceLib,并在里面创建一个TestLib.cs的类,代码如下:

viewsource

print?

01
using
System.ComponentModel;
02
using
System.Data;
03
using
FluorineFx;
04
05
namespace
ServiceLib
06
{
07
[RemotingService]
08
[Description(
"TestService"
)]
09
public
class
TestLib
10
{
11
[DataTableType(
"SliverlightApp.Person"
)]
12
public
DataTableGetPersonList()
13
{
14
DataTabletbl=
new
DataTable();
15
tbl.Columns.Add(
"Name"
,
typeof
(
string
));
16
tbl.Columns.Add(
"Age"
,
typeof
(System.Int32));
17
tbl.Rows.Add(
"菩提树下的杨过"
,30);
18
tbl.Rows.Add(
"小龙女"
,100);
19
return
tbl;
20
}
21
}
22
}
当然,这个项目要引用FluorineFx程序集,该项目的主要用意在于把"取数据"的业务逻辑封装在这一层,以方便重用。

2、再创建一个webApplication,起名为WebApp,同样要添加FluorineFx.dll的引用

2.1然后创建一个名为Gateway.aspx的文件,这样就行了,不用添加任何多余的代码(这个文件作为调用FluorineFx的网关)

2.2在根目录下,创建目录Web-INF/flex(即二层目录),然后在flex目录下,放置一个services-config.xml,内容如下:

viewsource

print?

01
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
02
<
services-config
>
03
<
services
>
04
<
service
id
=
"remoting-service"
class
=
"flex.messaging.services.RemotingService"
05
06
messageTypes
=
"flex.messaging.messages.RemotingMessage"
>
07
<
destination
id
=
"fluorine"
>
08
<
channels
>
09
<
channel
ref
=
"my-amf"
/>
10
</
channels
>
11
<
properties
>
12
<
source
>*</
source
>
13
</
properties
>
14
</
destination
>
15
</
service
>
16
</
services
>
17
18
<
channels
>
19
<
channel-definition
id
=
"my-amf"
class
=
"mx.messaging.channels.AMFChannel"
>
20
<
endpoint
uri
=
"http://{server.name}:{server.port}/{context.root}/Gateway.aspx"
21
22
class
=
"flex.messaging.endpoints.AMFEndpoint"
/>
23
<
properties
>
24
<!--<legacy-collection>true</legacy-collection>-->
25
</
properties
>
26
</
channel-definition
>
27
</
channels
>
28
</
services-config
>
照抄就好了,不用管太多。基本上这个配置的作用就相当于添加wcf(svc文件)后,系统自动在web.config中增加的配置节点,用于提供一些必要的配置信息.

2.3修改web.config的httpModules节点为以下内容

viewsource

print?

1
<
httpModules
>
2
<
add
name
=
"ScriptModule"
type
=
"System.Web.Handlers.ScriptModule,System.Web.Extensions,Version=3.5.0.0,Culture=neutral,PublicKeyToken=31BF3856AD364E35"
/>
3
<
add
name
=
"FluorineGateway"
type
=
"FluorineFx.FluorineGateway,FluorineFx"
/>
4
</
httpModules
>
2.4添加对ServiceLib项目的引用

3、最后创建一个silverlight项目,添加FluorineFx.dll引用,命名为SliverlightApp

注意:FluorineFx.dll有二个版本,一个用于webform,一个专用于silverlight(本文最后会给出下载)

通常用vs.net创建一个silverlight项目时,会提示你是否把该项目承载于一个webApplication项目中,以方便测试,这里直接指定第2步中的webApp为承载项目(即相当于webApp项目添加对SliverlightApp的引用)

在silverlight中访问FluorineFx的关键代码如下:

viewsource

print?

01
using
FluorineFx;
02
using
FluorineFx.AMF3;
03
using
FluorineFx.Messaging.Api.Service;
04
using
FluorineFx.Net;
05
06
...
07
08
//点击按钮时,开始调用
09
private
void
btnFluorineFx_Click(
object
sender,RoutedEventArgse)
10
{
11
NetConnection_netConnection=
new
NetConnection();
12
_netConnection.ObjectEncoding=ObjectEncoding.AMF3;
13
_netConnection.NetStatus+=
new
NetStatusHandler(_netConnection_NetStatus);
14
_netConnection.Connect(
"http://localhost:1718/Gateway.aspx"
);
15
_netConnection.Call(
"ServiceLib.TestLib.GetPersonList"
,
new
GetPersonHandler(
this
));
16
17
}
18
19
//状态回调
20
private
void
_netConnection_NetStatus(
object
sender,NetStatusEventArgse)
21
{
22
string
level=e.Info[
"level"
]
as
string
;
23
this
.Dispatcher.BeginInvoke(()=>{
this
.txtResult.Text=
"level:"
+level+
",code:"
+e.Info
24
25
[
"code"
]
as
String;});
26
27
}
28
29
30
//数据回调处理
31
private
class
GetPersonHandler:IPendingServiceCallback
32
{
33
MainPagepage;
34
35
public
GetPersonHandler(MainPagepage)
36
{
37
this
.page=page;
38
}
39
40
public
void
ResultReceived(IPendingServiceCallcall)
41
{
42
page.Dispatcher.BeginInvoke(()=>{page.txtResult.Text=
""
;});
43
object
result=call.Result;
44
ArrayCollectionitems=result
as
ArrayCollection;
45
foreach
(
object
item
in
items)
46
{
47
Personp=item
as
Person;
//注意:这里直接将数据反序列化为Person了
48
49
page.Dispatcher.BeginInvoke(()=>{page.txtResult.Text+=p.ToString()+
";"
;});
50
51
52
}
53
54
}
55
}
当然还有一个数据实体类Person.cs

viewsource

print?

01
using
System;
02
03
namespace
SliverlightApp
04
{
05
public
class
Person
06
{
07
public
string
Name{
set
;
get
;}
08
public
int
Age{
set
;
get
;}
09
10
public
override
string
ToString(){
11
return
"name:"
+Name+
",age:"
+Age.ToString();
12
}
13
}
14
}
4、最后回过头来,在webApp中把(创建silverlight项目时自动生成的)SliverlightAppTestPage.aspx设置为启动页测试就行了

整个解决方案的目录结构如下:



分析:

传统的soap协议是采用xml格式的,而xml格式的最大问题就是数据太大,比如一个普通的"helloworld"字符串,经过xml格式封装后,可能变成<string>helloworld</string>,再加上文件头部的xml文档声明,传输数据量最终会增加不少。

为了改进,Adobe发明了AMF0/AMF3协议,AMF是Adobe独家开发出来的通信协议,它采用二进制压缩,序列化、反序列化、传输数据,从而为Flash播放器与FlashRemoting网关通信提供了一种轻量级的、高效能的通信方式。

所以FluorineFx相对于基于soap协议的webservice/wcf而言,应该是效率会更高,不过我们也应该看到微软的进步:wcf在传输数据时,除了xml格式,还可以用json格式甚至直接最原始的stream流格式。为了比较,我在代码中还特意加了test.svc以json格式返回数据,用于跟fluorinefx做下对比比(xml格式就懒得比较了,传输数据量肯定要大于json格式)--test.svc里的具体代码如下:

viewsource

print?

001
using
System.Collections.Generic;
002
using
System.Data;
003
using
System.IO;
004
using
System.ServiceModel;
005
using
System.ServiceModel.Activation;
006
using
System.ServiceModel.Web;
007
using
System.Text;
008
009
namespace
WebApp
010
{
011
[ServiceContract(Namespace=
""
)]
012
[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
013
public
class
test
014
{
015
016
///<summary>
017
///利用系统自动封装成json格式
018
///</summary>
019
///<returns></returns>
020
[OperationContract]
021
[WebInvoke(Method=
"GET"
,ResponseFormat=WebMessageFormat.Json)]
022
public
List<SliverlightApp.Person>GetPersonList()
023
{
024
List<SliverlightApp.Person>lst=
new
List<SliverlightApp.Person>();
025
lst.Add(
new
SliverlightApp.Person(){Name=
"菩提树下的杨过"
,Age=30});
026
lst.Add(
new
SliverlightApp.Person(){Name=
"小龙女"
,Age=100});
027
return
lst;
028
}
029
030
///<summary>
031
///自己封装成json格式
032
///</summary>
033
///<returns></returns>
034
[OperationContract]
035
[WebInvoke(Method=
"GET"
)]
036
public
StreamGetPersonList2()
037
{
038
DataTabletbl=
new
DataTable();
039
tbl.Columns.Add(
"Name"
,
typeof
(
string
));
040
tbl.Columns.Add(
"Age"
,
typeof
(System.Int32));
041
tbl.Rows.Add(
"菩提树下的杨过"
,30);
042
tbl.Rows.Add(
"小龙女"
,100);
043
return
GetStream(CreateJsonParameters(tbl));
044
}
045
046
047
///<summary>
048
///将datatable转化成json字符串
049
///</summary>
050
///<paramname="dt"></param>
051
///<returns></returns>
052
private
string
CreateJsonParameters(DataTabledt)
053
{
054
StringBuilderJsonString=
new
StringBuilder();
055
if
(dt!=
null
&&dt.Rows.Count>0)
056
{
057
JsonString.Append(
"{"
);
058
JsonString.Append(
"\"Head\":["
);
059
for
(
int
i=0;i<dt.Rows.Count;i++)
060
{
061
JsonString.Append(
"{"
);
062
for
(
int
j=0;j<dt.Columns.Count;j++)
063
{
064
if
(j<dt.Columns.Count-1)
065
{
066
JsonString.Append(
"\""
+dt.Columns[j].ColumnName.ToString().Replace(
"\""
,
"\\\""
)+
"\":"
+
"\""
+dt.Rows[i][j].ToString().Replace(
"\""
,
"\\\""
)+
"\","
);
067
}
068
else
if
(j==dt.Columns.Count-1)
069
{
070
JsonString.Append(
"\""
+dt.Columns[j].ColumnName.ToString().Replace(
"\""
,
"\\\""
)+
"\":"
+
"\""
+dt.Rows[i][j].ToString().Replace(
"\""
,
"\\\""
)+
"\""
);
071
}
072
}
073
074
if
(i==dt.Rows.Count-1)
075
{
076
JsonString.Append(
"}"
);
077
}
078
else
079
{
080
JsonString.Append(
"},"
);
081
}
082
}
083
JsonString.Append(
"]}"
);
084
return
JsonString.ToString();
085
}
086
else
087
{
088
return
null
;
089
}
090
}
091
092
///<summary>
093
///辅助方法,用于输出流
094
///</summary>
095
///<paramname="str"></param>
096
///<returns></returns>
097
private
StreamGetStream(
string
str)
098
{
099
MemoryStreamms=
new
MemoryStream();
100
StreamWritersw=
new
StreamWriter(ms);
101
sw.AutoFlush=
true
;
102
sw.Write(str);
103
ms.Position=0;
104
WebOperationContext.Current.OutgoingResponse.ContentType=
"text/plain"
;
105
return
ms;
106
}
107
}
108
}


这是用httpwatch在firefox下测试的结果:

如果用最原始的stream方法封装json数据,返回的数据为



如果用系统提供的json自动封装,返回的数据为



而FluorineFx是以二进制返回的,不方便直接观察字符串,只能直接反序列化为Peron类,就不贴出结果了。

从运行图的Received列上可以看出:“FluorineFx返回的数据大小-375”要小于“wcf默认封装的json数据-389”,但大于“开发者自行处理的json数据大小-312”

再比较Time列,FluorineFx所用的时间是最小的(当然多测试几次,结果稍有不同,但经过我的多次观察,FluorineFx所花的时间始终是最小的)

综合比较下来:FluorineFx传输的数量小,传输时间短,整体效率是不错的,确实是silverlight/.net与其它系统高效传输数据的可选方式之一。

文中所用源代码下载:http://cid-2959920b8267aaca.office.live.com/self.aspx/Silverlight/FluorineFx.rar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: