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>
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次异步操作如果同时操作无法确定哪次先完成,哪次后完成。完全是靠技巧,想到了就很简单,没想到就感觉非常复杂、无法控制):
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
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>
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服务(如何控制二次异步操作的先后顺序,想到了就很简单)
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属性如下:
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了,并且解除了耦合关系,实现了松耦合。当服务端(特别是数据库字段)变了之后,亦或者是类名都变了的时候,我们只需要对属性名称进行相应的修改即可。如下述代码所示:
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。
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> 中,
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类),如下
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相关的一些总结
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架