研究silverlight wcf陆陆续续有一段时间了,由于一直不太适应传统的引用做法,所以想封装出一个简单的方法进行silverlight 和wcf之间通讯。
大家都知道,如果是控制台程序或者是winform程序,在程序中要发布一个wcf很简单,可以开辟一个线程,在登录或者主窗口初始化的时候把服务开启即可,
但是对于silverlight比较特殊,程序已启动,就直接加载SL客户端,在启动的时候,在服务端要做一些事情不太容易,而你等silverlight再想做服务端的事情时,
就没那么简单了,必须要用通讯。
传统的wcf引用的方法是,在web目录新建一个wcf服务,会自动生成一个svc文件和一个对应的服务接口,右键svc文件,选择在浏览器中运行,然后再SL客户端
现在引用服务,弹出窗口中点发现,然后会自动生成一个客户端,和配置文件。然后再客户端使用的时候创建客户端实例,然后再调用同步方法和方法回调。这种方
法不易于维护和管理,工作起来也挺别扭。所以,我决定封装起来,尽量把这个通道简化一点。
下面是项目的结构
一:服务端Service
有2个服务,每个服务分别一个接口
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace Service { [ServiceContract] public interface ITestService { [OperationContract] void DoWork(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Service { public class TestService : ITestService { public void DoWork() { } } }
第二个接口和实现,返回字符串
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace Service { [ServiceContract] public interface ITestService2 { [OperationContract] string GetData(string str); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Service { public class TestService : ITestService { public void DoWork() { } } }
OK,服务端定义和实现就这些,
然后看web项目怎么配置吧,首先先把服务类库引用过来,然后添加两个wcf,名称和服务实现类名一致,把自动生成的接口文件删除,以及.cs文件也删除,然后双击打开svc文件,改成如下配置。
如果没有需求,web层的工作到此就结束了。最后看客户端是怎么实现的,客户端有2个异步服务接口,其实,通用引用方式引用服务的时候,也会自动生成对应的接口,一个开始begin,和一个结束end,不过,我在这里给服务特性加了一个configname属性,用来匹配svc文件,所以ConfigurationName要等于你的svc文件名一致,看下面的代码吧。
using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.ServiceModel; namespace Service { [ServiceContract(ConfigurationName = "TestService")] public partial interface ITestService { [OperationContract(AsyncPattern = true)] IAsyncResult BeginDoWork(AsyncCallback callback, object state); void EndDoWork(IAsyncResult result); } }
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.ServiceModel; namespace Service { [ServiceContract(ConfigurationName = "TestService2")] public partial interface ITestService2 { [OperationContract(AsyncPattern = true)] IAsyncResult BeginGetData(string str, AsyncCallback callback, object state); string EndGetData(IAsyncResult result); } }
客户端定义就到此结束,最后还剩下一个使用wcf,留到最后讲,下面看代理类,客户端只要传递服务接口,就可以返回一个代理类实例,然后使用服务的开始方法和结束方法,
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.ServiceModel; using System.ServiceModel.Channels; using System.Linq; namespace Proxy { public class Proxy { /// <summary> /// 根据接口创建一个wcf接口实例 /// </summary> /// <typeparam name="T">服务接口</typeparam> /// <returns>接口实例</returns> public static T GetService<T>() { var xapUri = Application.Current.Host.Source; //web路径 var webUri = new Uri(xapUri, "../"); string webpath = webUri.ToString(); Type type = typeof(T); //wcf服务全路径 string url = ""; //获取接口契约的特性 object[] atbs = type.GetCustomAttributes(typeof(ServiceContractAttribute), false); if (atbs.Any(cp => cp.GetType() == typeof(ServiceContractAttribute))) { ServiceContractAttribute abt = atbs.First(cp => cp.GetType() == typeof(ServiceContractAttribute)) as ServiceContractAttribute; string servicepath = abt.ConfigurationName; url = webpath + servicepath; } //生成接口实例 if (!string.IsNullOrEmpty(url)) { EndpointAddress epAddress = new EndpointAddress(url + ".svc"); Binding binding = new BasicHttpBinding(); ChannelFactory<T> factory = new ChannelFactory<T>(binding, epAddress); T client = factory.CreateChannel(); return client; } else return default(T); } } }
在客户端项目中,把这个代理类库引用进来,就可以使用了,使用代码如下
private void button1_Click(object sender, RoutedEventArgs e) { ITestService client = Proxy.Proxy.GetService<ITestService>(); client.BeginDoWork((result) => { Deployment.Current.Dispatcher.BeginInvoke(() => { MessageBox.Show("第一个服务执行完毕"); }); }, null); ITestService2 client2 = Proxy.Proxy.GetService<ITestService2>(); client2.BeginGetData("123", (result) => { Deployment.Current.Dispatcher.BeginInvoke(() => { MessageBox.Show(client2.EndGetData(result)); }); }, null); }
到此,这个工作就结束了。
这样做的好处就是,你不需要面对一堆的配置文件,只需要定义接口和实现,也易于再次对接口的封装,使用起来也方便,只要对代理类传递接口就可以得到一个服务实例,不用关心服务如何配置,如何实现。总之,我觉得还挺适合我,不知道对各位有没有用,欢迎大家来讨论和拍砖!!! 最后感谢一些一个叫“飞”的网友,他帮我解决了一些谜团。