研究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);
}
到此,这个工作就结束了。
这样做的好处就是,你不需要面对一堆的配置文件,只需要定义接口和实现,也易于再次对接口的封装,使用起来也方便,只要对代理类传递接口就可以得到一个服务实例,不用关心服务如何配置,如何实现。总之,我觉得还挺适合我,不知道对各位有没有用,欢迎大家来讨论和拍砖!!! 最后感谢一些一个叫“飞”的网友,他帮我解决了一些谜团。