zoukankan      html  css  js  c++  java
  • 转 silverlight获取外部数据的另一种选择:FluorineFx

    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交互提供了可能。(详见百度百科FluorineFX http://baike.baidu.com/view/1654458.htm?fr=ala0_1)


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

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

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

    01 using System.ComponentModel;
    02 using System.Data;
    03 using FluorineFx;
    04   
    05 namespace ServiceLib
    06 {
    07     [RemotingService]
    08     [Description("Test Service")]
    09     public class TestLib
    10     {
    11         [DataTableType("SliverlightApp.Person")]
    12         public DataTable GetPersonList()
    13         {
    14             DataTable tbl = 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,内容如下:

    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节点为以下内容 

    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的关键代码如下:

    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, RoutedEventArgs e)
    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, NetStatusEventArgs e)
    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             MainPage page;
    34   
    35             public GetPersonHandler(MainPage page)
    36             {
    37                 this.page = page;
    38             }
    39   
    40             public void ResultReceived(IPendingServiceCall call)
    41             {
    42                 page.Dispatcher.BeginInvoke(() => { page.txtResult.Text = ""; });
    43                 object result = call.Result;
    44                 ArrayCollection items = result as ArrayCollection;
    45                 foreach (object item in items)
    46                 {
    47                     Person p = item as Person;//注意:这里直接将数据反序列化为Person了
    48   
    49                     page.Dispatcher.BeginInvoke(() => { page.txtResult.Text += p.ToString() + ";"; });          
    50   
    51             
    52                 }
    53   
    54             }
    55         }

    当然还有一个数据实体类Person.cs

    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格式的最大问题就是数据太大,比如一个普通的"hello world"字符串,经过xml格式封装后,可能变成<string>hello world</string>,再加上文件头部的xml文档声明,传输数据量最终会增加不少。

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

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

    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 Stream GetPersonList2()
    037         {
    038             DataTable tbl = 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         /// <param name="dt"></param>
    051         /// <returns></returns>
    052         private string CreateJsonParameters(DataTable dt)
    053         {            
    054             StringBuilder JsonString = 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         /// <param name="str"></param>
    096         /// <returns></returns>
    097         private Stream GetStream(string str)
    098         {
    099             MemoryStream ms = new MemoryStream();
    100             StreamWriter sw = 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

  • 相关阅读:
    HTTP协议
    从Iterator到async/await
    那些年曾谈起的跨域
    设计模式之观察者模式与发布订阅模式
    移动Web深度剖析
    浅析JavaScript异步
    mySql入门-(二)
    C# WEB项目MVC框架原理及约定
    Dynamics CRM 邮箱设置 “允许使用凭据进行电子邮件处理” 被禁用的解决
    Win10系统恢复IE 11浏览器
  • 原文地址:https://www.cnblogs.com/chuncn/p/1786192.html
Copyright © 2011-2022 走看看