Web Service的一些经验和技巧总结

      先看整体项目布局(如下图所示),有个大体的了解。Jasen.SilverlightService为silverlight项目,Jasen.SilverlightService.Core为实现松耦合的类库,Jasen.SilverlightService.Web为Web服务发布网站。本文将讲解web服务的注意事项以及使用技巧。这是本人在开发中的一些经验以及总结,本来是需要通过WEB服务获取相关的2个数据,然后进行算法处理的(采用职责链设计模式设计路径算法),这里仅仅是大体框架而已,希望本文能够对读者有一定的帮助。

(一)创建Web Service服务

        以前总喜欢使用接口来进行编码,但是这里得注意了,Web服务方法的返回类型是不允许使用接口的,如不能使用IList<T>类型等等(经验之谈而已,免得到时候代码全部需要修改)而且该类型T必须是可序列化的,还有一点就是类型如果有参数的构造函数,必须显示实现无参构造函数。

 

        按照下列顺序创建web服务(可以发现属性的get;set;所产生的影响):

(1) 先在Jasen.SilverlightService.Web里定义一个实体类,我将SmallTitle(string)、IsSucceed(bool)设置为只读的类型并且赋初始值,其他的设置为自动属性{get;set;}

代码
 /// <summary>
    
/// 
    
/// </summary>
    public class ServerInfo
    {
        
private bool _isSucceed = true;
        
private string _smallTitle = "small title";

        
/// <summary>
        
/// 
        
/// </summary>
        public string SmallTitle
        {
            
get

            {
                
return _smallTitle;
            }
        }


        
/// <summary>
        
/// 
        
/// </summary>
        public string Title
        {
            
get;
            
set;
        }

        
/// <summary>
        
/// 
        
/// </summary>
        public string Content
        {
            
get;
            
set;
        }

        
/// <summary>
        
/// 
        
/// </summary>
        public bool IsSucceed
        {
            
get
            {
                
return _isSucceed;
            }
        }

        
/// <summary>
        
/// 
        
/// </summary>
        public bool IsPublished
        {
            
get;
            
set;
        }
    }

(2) 然后我们在Jasen.SilverlightService.Web里面创建一个web服务(其实在这里创建是不太合理的,主要是为了简便),命名为InfoService,如下图所示意。

(3)在InfoService.cs编写相应的web服务方法,代码如下(为什么定义2个类似的方法,主要是针对后面异步操作的问题,2次异步操作如果同时操作无法确定哪次先完成,哪次后完成。完全是靠技巧,想到了就很简单,没想到就感觉非常复杂、无法控制):

 5         [WebMethod]
 6         public List<ServerInfo> GetFirstInfos()
 7         {
 8             List<ServerInfo> infos = new List<ServerInfo>()
 9             {
10             new ServerInfo{ Title="Title1", Content="Jasen",IsPublished=true  },
11             new ServerInfo{ Title="Title2", Content="Jasen",IsPublished=true },
12             new ServerInfo{ Title="Title3", Content="Jasen" },
13             new ServerInfo{ Title="Title4", Content="Jasen" },
14             };
15 
16             return infos;
17         }
18 
23         [WebMethod]
24         public List<ServerInfo> GetSecondInfos()
25         {
26             List<ServerInfo> infos = new List<ServerInfo>()
27             {
28             new ServerInfo{ Title="Title1", Content="Jasen2",IsPublished=true },
29             new ServerInfo{ Title="Title2", Content="Jasen2" },
30             new ServerInfo{ Title="Title3", Content="Jasen2" },
31             new ServerInfo{ Title="Title4", Content="Jasen2" },
32             };
33 
34             return infos;
35         }  

(4)右键单击InfoService.asmx,在弹出的快捷菜单中单击“在浏览器中查看”,弹出如下窗口:

(5)单击上面中的任一的一个方法,弹出如下界面:

(6)单击调用,我们将发现如下情况,SmallTitle(string)、IsSucceed(bool)为只读类型(只有get方法)没有显示相关信息,只有同时有get,set方法的属性才会有相关的信息显示(这里需要注意了)。如下图绿色标识框中所示:

(二)创建一个ClientInfo类(主要是针对ServerInfo类,与之相区别),然后添加相应的web service引用到Silverlight项目中

代码
/// <summary>
    
/// 
    
/// </summary>
    public class ClientInfo
    {
        
/// <summary>
        
/// 
        
/// </summary>
        public string Title
        {
            
get;
            
set;
        }

        
/// <summary>
        
/// 
        
/// </summary>
        public string Content
        {
            
get;
            
set;
        }

        
/// <summary>
        
/// 
        
/// </summary>
        public bool IsPublished
        {
            
get;
            
set;
        }
    }

(三)页面中使用Web服务(如何控制二次异步操作的先后顺序,想到了就很简单)

 

 1 public partial class MainPage : UserControl
 2     {
 3         InfoServiceSoapClient client;
 4 
 5         public MainPage()
 6         {
 7             InitializeComponent();
 8             InitializeService();
 9         }
10 
11         /// <summary>
12         /// 
13         /// </summary>
14         private ObservableCollection<ServerInfo> FirstInfos
15         {
16             get;
17             set;
18         }
19 
20         /// <summary>
21         /// 
22         /// </summary>
23         private ObservableCollection<ServerInfo> SecondInfos 
24         {
25             get
26             set;
27         }
28 
29         /// <summary>
30         /// 
31         /// </summary>
32         private void InitializeService()
33         {
34             client = new InfoServiceSoapClient();
35             client.GetFirstInfosCompleted += new EventHandler<GetFirstInfosCompletedEventArgs>(client_GetFirstInfosCompleted);
36             client.GetFirstInfosAsync();
37         }
38 
39         /// <summary>
40         /// 
41         /// </summary>
42         /// <param name="sender"></param>
43         /// <param name="e"></param>
44         void client_GetFirstInfosCompleted(object sender, GetFirstInfosCompletedEventArgs e)
45         {
46             if (e.Result != null && e.Error == null)
47             {
48                 this.FirstInfos = e.Result;
49                 client.GetSecondInfosCompleted += new EventHandler<GetSecondInfosCompletedEventArgs>(client_GetSecondInfosCompleted);
50                 client.GetSecondInfosAsync();
51             }
52         }
53 
54         /// <summary>
55         /// 
56         /// </summary>
57         /// <param name="sender"></param>
58         /// <param name="e"></param>
59         void client_GetSecondInfosCompleted(object sender, GetSecondInfosCompletedEventArgs e)
60         {
61             if (e.Result != null && e.Error == null)
62             {
63                 this.SecondInfos = e.Result;
64                 if (this.FirstInfos != null)
65                 {
66                     ClientInfoHandler<ServerInfo> handler = new ClientInfoHandler<ServerInfo>();
67                     List<ClientInfo> clientInfos = handler.Convert(this.FirstInfos, this.SecondInfos);
68 
69                     dataGrid.ItemsSource = clientInfos;
70                 }
71             }
72         }
73     }

(1)我们发现如下代码被标成红色

          void client_GetFirstInfosCompleted(object sender, GetFirstInfosCompletedEventArgs e)
         {
             if (e.Result != null && e.Error == null)
             {

                this.FirstInfos = e.Result;
                client.GetSecondInfosCompleted += new EventHandler<GetSecondInfosCompletedEventArgs>(client_GetSecondInfosCompleted);                

                client.GetSecondInfosAsync();            

              }
         }
在这里设置this.FirstInfos = e.Result;  ,然后再次异步调用第二个方法, client.GetSecondInfosAsync();  --->这样我们就能够控制获取数据的时间先后顺序。

 

(2)在第二次异步调用的回调方法中,我们将处理从web服务返回的数据。

      void client_GetSecondInfosCompleted(object sender, GetSecondInfosCompletedEventArgs e)
      {

          handler.Convert(this.FirstInfos, this.SecondInfos);//在这里处理相应从web服务返回的数据。类型为ObservableCollection<ServerInfo>

      }


(3)
GetSecondInfosCompletedEventArgs的Result属性如下:

 public System.Collections.ObjectModel.ObservableCollection<Jasen.SilverlightService.InfoService.ServerInfo> Result {
            
get {
                
base.RaiseExceptionIfNecessary();
                
return ((System.Collections.ObjectModel.ObservableCollection<Jasen.SilverlightService.InfoService.ServerInfo>)(this.results[0]));
            }
        }

(四)如何解除耦合(采用反射将ServerInfo转换为ClientInfo:松耦合)

 

     返回类型为System.Collections.ObjectModel.ObservableCollection<Jasen.SilverlightService.InfoService.ServerInfo>,为了解除耦合,定义一个ClientInfoHandler<T>泛型类来处理需要转换的数据。

      public List<ClientInfo> Convert(ObservableCollection<T> firstCollection,  ObservableCollection<T> secondCollection)
      {

      }
在这一步里,
我们主要是通过反射来获取属性的值 object result = type.InvokeMember(propertyName, invokeAttr, null, item, args);更多信息请参考MSDN。

 

这样我们就将ServerInfo转换为ClientInfo了,并且解除了耦合关系,实现了松耦合。当服务端(特别是数据库字段)变了之后,亦或者是类名都变了的时候,我们只需要对属性名称进行相应的修改即可。如下述代码所示:

  1 namespace Jasen.SilverlightService.Core
  2 {
  3     public class ClientInfoHandler<T>
  4     {
  5         /// <summary>
  6         /// 
  7         /// </summary>
  8         /// <param name="firstCollection"></param>
  9         /// <param name="secondCollection"></param>
 10         /// <returns></returns>
 11         public List<ClientInfo> Convert(ObservableCollection<T> firstCollection,
 12             ObservableCollection<T> secondCollection)
 13         {
 14             if (firstCollection == null || secondCollection == null ||
 15                 firstCollection.Count == 0 || secondCollection.Count == 0)
 16             {
 17                 return null;
 18             }
 19 
 20             List<ClientInfo> firstList = new List<ClientInfo>();
 21             List<ClientInfo> secondList = new List<ClientInfo>();
 22 
 23             foreach (T item in firstCollection)
 24             {
 25                 //反射获取属性值
 26                 ClientInfo info = ReflectItem(item);
 27                 if (info != null)
 28                 {
 29                     firstList.Add(info);
 30                 }
 31             }
 32 
 33             foreach (T item in secondCollection)
 34             {
 35                 //反射获取属性值
 36                 ClientInfo info = ReflectItem(item);
 37                 if (info != null)
 38                 {
 39                     secondList.Add(info);
 40                 }
 41             }
 42 
 43 
 44             return ConvertList(firstList,secondList);
 45         }
 46 
 47         /// <summary>
 48         /// 本来这里向写个算法什么的,还是算了,就相当于对2个数据进行操作就可以了
 49         /// </summary>
 50         /// <param name="firstList"></param>
 51         /// <param name="secondList"></param>
 52         /// <returns></returns>
 53         private List<ClientInfo> ConvertList(List<ClientInfo> firstList, List<ClientInfo> secondList)
 54         {
 55            if(firstList==null || secondList==null||firstList.Count==0||secondList.Count==0)
 56            {
 57                return null;
 58            }
 59 
 60            foreach (ClientInfo item in secondList)
 61            {
 62                firstList.Add(item);
 63            }
 64 
 65            return firstList;
 66         }
 67 
 68         /// <summary>
 69         /// 
 70         /// </summary>
 71         /// <param name="item"></param>
 72         /// <returns></returns>
 73         private ClientInfo ReflectItem(object item)
 74         {
 75             ClientInfo info = new ClientInfo();
 76             BindingFlags flags = BindingFlags.Public | BindingFlags.Instance 
 77                 | BindingFlags.GetProperty;
 78           
 79             info.Content = GetPropertyValue(item, "Content", flags , null);
 80             info.Title = GetPropertyValue(item, "Title", flags, null);
 81             if (string.Equals(GetPropertyValue(item, "IsPublished", flags, null),
 82                 "TRUE", StringComparison.InvariantCultureIgnoreCase))
 83             {
 84                 info.IsPublished = true;
 85             }
 86             else 
 87             {
 88                 info.IsPublished = false;
 89             }
 90 
 91             return info;
 92         }
 93 
 94         /// <summary>
 95         /// 
 96         /// </summary>
 97         /// <param name="item"></param>
 98         /// <param name="propertyName"></param>
 99         /// <param name="invokeAttr"></param>
100         /// <param name="args"></param>
101         /// <returns></returns>
102         private static string GetPropertyValue(object item, string propertyName,
103             BindingFlags invokeAttr, object[] args)
104         {
105             Type type = item.GetType();
106             string value = "";
107 
108             try
109             {
110                 object result = type.InvokeMember(propertyName, invokeAttr,
111                     null, item, args);
112 
113                 if (result != null)
114                 {
115                     value = result.ToString().Trim();
116                 }
117             }
118             catch (MissingMethodException ex)
119             { 
120                
121             }
122 
123             return value;
124         }
125     }
126 }

  

 如果我们将 public List<ClientInfo> Convert(ObservableCollection<T> firstCollection,  ObservableCollection<T> secondCollection){}
改为public List<ClientInfo> Convert(List<ServerInfo> firstCollection,  List<ServerInfo> secondCollection){}

那么耦合太紧了,服务端一修改,处理类ClientInfoHandler就得改很多。采用范型和反射来操作的话,我们仅仅需要修改的是属性名称而已。

 

当然,你也可以同时将2个数据组装在一个类中,然后在一个Web服务方法中返回该类。界面显示如下(本来我的是算法路径图,哈哈)

 

(五)另附一种传递参数的方式(通过事件和委托)

 

 (1)我们继续添加一个silverlight类库以及一个App应用程序,如下所示:

(2)先看InfoProxy代理类,代理Web服务的处理,以及传递给UI。

 1     public delegate void RouteEventHandler(object sender, RouteEventArgs<ServerInfo> args);
 2 
 3     public class InfoProxy
 4     {
 5 
 6         /// <summary>
 7         /// 
 8         /// </summary>
 9         public event RouteEventHandler OnRetrieve;
10 
11         /// <summary>
12         /// 
13         /// </summary>
14         public InfoProxy()
15         {
16 
17         }
18 
19         /// <summary>
20         /// 
21         /// </summary>
22         public void HandlerData() 
23         {
24             InfoServiceSoapClient client = new InfoServiceSoapClient();
25             client.GetFirstInfosCompleted += new EventHandler<GetFirstInfosCompletedEventArgs>(client_GetFirstInfosCompleted);
26             client.GetFirstInfosAsync();
27         }
28 
29         void client_GetFirstInfosCompleted(object sender, GetFirstInfosCompletedEventArgs e)
30         {
31             if (e.Result != null&&e.Error==null)
32             {
33                 List<ServerInfo> items = new List<ServerInfo>();
34                 foreach (var item in e.Result)
35                 {
36                     if (item != null)
37                     {
38                         items.Add(new ServerInfo()
39                         {
40                             Content = item.Content,
41                             Title = item.Title
42                         });
43                     }
44                 }
45                 // 这里是可以对ServerInfo进行处理的,进行算法处理----------处理后再传给UI
46                 //现在这里是没有进行处理,直接传给UI的---------控制
47                 RouteEventArgs<ServerInfo> args = new RouteEventArgs<ServerInfo>();
48                 args.Result = items.ToArray();
49                 args.Error = e.Error;
50                 this.OnRetrieve(sender, args);
51             }
52         }
53     }

 在  void client_GetFirstInfosCompleted(object sender, GetFirstInfosCompletedEventArgs e){}方法里,将触发事件this.OnRetrieve(sender, args); 。我们将数据保存在参数类RouteEventArgs<T> 中,

 1  public class RouteEventArgs<T> : EventArgs
 2     {
 3         private T[] _result;
 4 
 5         /// <summary>
 6         /// 
 7         /// </summary>
 8         public T[] Result
 9         {
10             get
11             {
12                 return _result;
13             }
14             set
15             {
16                 _result = value;
17             }
18         }
19 
20         public Exception Error
21         {
22             get;
23             set;
24         }
25     }

(3)在UI层,我们通过如下代码对WEB服务进行调用(通过InfoProxy类),如下

 1   public MainPage()
 2         {
 3             InitializeComponent();
 4             InfoProxy proxy = new InfoProxy();
 5             proxy.OnRetrieve += new RouteEventHandler(proxy_OnRetrieve);
 6             proxy.HandlerData();
 7         }
 8 
 9         void proxy_OnRetrieve(object sender, RouteEventArgs<Core.InfoService.ServerInfo> args)
10         {
11             if (args.Result != null&&args.Error==null)
12             {
13                 dataGrid.ItemsSource  = args.Result;
14             }
15         }

先将事件绑定,然后再调用proxy.HandlerData();。避免事件为NULL。

(4)记得加上访问策略文件crossdomain.xml

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-access-from domain="*" />
  <allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>

显示如下

希望本文对各位有所帮助,源代码下载地址:Web Services相关的一些总结

原文地址:https://www.cnblogs.com/jasenkin/p/silverlight_webservice.html