Silverlight跨域调用服务层的WCF Ria Service,但有个不完美的地方:Silverlight客户端添加WCF Ria Service的服务引用是死的,部署的时候怎么办?也就是WCF Ria Service的地址必须是可配置的,动态的。
本文给出解决方案。Silverlight客户端添加WCF Ria Service的服务引用后,会自动生成一个ServiceReferences.ClientConfig文件,内容大致如下:
- <configuration>
- <system.serviceModel>
- <bindings>
- <customBinding>
- <binding name="CustomBinding_SLEnalbedWcfService1">
- <binaryMessageEncoding />
- <httpTransport maxReceivedMessageSize="2147483647"
- maxBufferSize="2147483647" />
- </binding>
- </customBinding>
- </bindings>
- <client>
- <endpoint address="http://localhost:7236/SLEnalbedWcfService1.svc"
- binding="customBinding" bindingConfiguration=
- "CustomBinding_SLEnalbedWcfService1"
- contract="ServiceReference1.SLEnalbedWcfService1"
- name="CustomBinding_SLEnalbedWcfService1" />
- </client>
- </system.serviceModel>
- </configuration>
<configuration> <system.serviceModel> <bindings> <customBinding> <binding name="CustomBinding_SLEnalbedWcfService1"> <binaryMessageEncoding /> <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" /> </binding> </customBinding> </bindings> <client> <endpoint address="http://localhost:7354/SLEnalbedWcfService1.svc" binding="customBinding" bindingConfiguration= "CustomBinding_SLEnalbedWcfService1" contract="ServiceReference1.SLEnalbedWcfService1" name="CustomBinding_SLEnalbedWcfService1" /> </client> </system.serviceModel> </configuration>
里面就有写死的endpoint地址,如果我们要动态设置服务地址,这个文件必须要干掉。OK,首先在VS2010中删除这个文件,然后运行下,肯定报错,没关系,我们首先建立服务工具类:ServiceUtil.cs,代码如下:
- 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 SilverlightApplication1.ServiceReference1;
- using System.ServiceModel;
- namespace SilverlightApplication1
- {
- public class ServiceUtil
- {
- public static SLEnalbedWcfService1Client GetServiceClient()
- {
- BasicHttpBinding binding = new BasicHttpBinding(
- Application.Current.Host.Source.Scheme.Equals("https",
- StringComparison.InvariantCultureIgnoreCase)
- ? BasicHttpSecurityMode.Transport :
- BasicHttpSecurityMode.None);
- binding.MaxReceivedMessageSize = int.MaxValue;
- binding.MaxBufferSize = int.MaxValue;
- return new SLEnalbedWcfService1Client(binding, new EndpointAddress(
- new Uri("http://localhost:7326/SLEnalbedWcfService1.svc*这个地址可以从配置文件读取*/, UriKind.Absolute))); //cross domain
- "
- /
- //new Uri(Application.Current.Host.Source,
- "../SLEnalbedWcfService1.svc"))); //non-cross domain
- }
- }
- }
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 SilverlightApplication1.ServiceReference1; using System.ServiceModel; namespace SilverlightApplication1 { public class ServiceUtil { public static SLEnalbedWcfService1Client GetServiceClient() { BasicHttpBinding binding = new BasicHttpBinding( Application.Current.Host.Source.Scheme.Equals("https", StringComparison.InvariantCultureIgnoreCase) ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.None); binding.MaxReceivedMessageSize = int.MaxValue; binding.MaxBufferSize = int.MaxValue; return new SLEnalbedWcfService1Client(binding, new EndpointAddress( new Uri("http://localhost:7326/SLEnalbedWcfService1.svc*这个地址可以从配置文件读取*/, UriKind.Absolute))); //cross domain " / //new Uri(Application.Current.Host.Source, "../SLEnalbedWcfService1.svc"))); //non-cross domain } } }
然后用这个东东来实例化Service,代码:
- SLEnalbedWcfService1Client client =
- ServiceUtil.GetServiceClient();
SLEnalbedWcfService1Client client = ServiceUtil.GetServiceClient();
运行一下,看看有没有错,如果有错,The remote server returned an error: NotFound. 看看原因在哪,然后看看是不是这个错误: The client and service bindings may be mismatched. 注意,跨域crossdomain.xml在服务端要有啊,或者clientaccesspolicy.xml也可以。
OK,如果你没有问题了,那就达到我们的目的了。注意:刚开始我们还是要添加web service引用的,否则得不到服务端WCF Ria Service接口类型,我们能动态配置的仅仅是服务端地址。(如果你WCF Ria Service的接口,svc名字等等都改了,那就要重新编译了。)
现在我们测试一下动态配置服务地址,假设我们的WCF Ria Service 移到另一个地址了。
如何模拟:在WCF Ria Service 的web application的项目属性里面的web属性页,修改一下port即可。
我们原来是自动分配的,7344端口,现在我们改为7355端口,svc地址也修改了。看看能不能动态调用WCF Ria Service呢。自己看看就ok了。
另:ServiceUtil类可以用generic(泛型)和反射 改良一下(老外原文):
- 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.Reflection;
- using System.ServiceModel;
- namespace SilverlightApplication1
- {
- /// <summary>
- /// sample:
- /// var client = ServiceUtil<serviceNamespace.SLEnalbedWcfService1Client>.GetInstance(ServiceType.Service1svc);
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public class ServiceUtil<T> where T : class
- {
- /// <summary>
- /// Service types
- /// </summary>
- public enum ServiceType
- {
- Service1svc
- }
- /// <summary>
- /// Create a service instance
- /// </summary>
- /// <param name="serviceType"></param>
- /// <returns></returns>
- public static T GetInstance(ServiceType serviceType)
- {
- T _instance = null;
- string serviceUri = GetServiceAddress(serviceType);
- if (string.IsNullOrEmpty(serviceUri)) return null;
- object[] paras = new object[2];
- BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
- binding.MaxBufferSize = int.MaxValue;
- binding.MaxReceivedMessageSize = int.MaxValue;
- EndpointAddress address = new EndpointAddress(new Uri(serviceUri, UriKind.Absolute));
- paras[0] = binding;
- paras[1] = address;
- ConstructorInfo constructor = null;
- try
- {
- Type[] types = new Type[2];
- types[0] = typeof(System.ServiceModel.Channels.Binding);
- types[1] = typeof(System.ServiceModel.EndpointAddress);
- constructor = typeof(T).GetConstructor(types);
- }
- catch (Exception)
- {
- return null;
- }
- if (constructor != null)
- _instance = (T)constructor.Invoke(paras);
- return _instance;
- }
- /// <summary>
- /// Get service address
- /// </summary>
- /// <param name="serviceType"></param>
- /// <returns></returns>
- public static string GetServiceAddress(ServiceType serviceType)
- {
- return "http://localhost:7354/SLEnalbedWcfService1.svc";//get from app.config is also ok
- }
- }
- }
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.Reflection; using System.ServiceModel; namespace SilverlightApplication1 { /// <summary> /// sample: /// var client = ServiceUtil<serviceNamespace.SLEnalbedWcfService1Client>.GetInstance(ServiceType.Service1svc); /// </summary> /// <typeparam name="T"></typeparam> public class ServiceUtil<T> where T : class { /// <summary> /// Service types /// </summary> public enum ServiceType { Service1svc } /// <summary> /// Create a service instance /// </summary> /// <param name="serviceType"></param> /// <returns></returns> public static T GetInstance(ServiceType serviceType) { T _instance = null; string serviceUri = GetServiceAddress(serviceType); if (string.IsNullOrEmpty(serviceUri)) return null; object[] paras = new object[2]; BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None); binding.MaxBufferSize = int.MaxValue; binding.MaxReceivedMessageSize = int.MaxValue; EndpointAddress address = new EndpointAddress(new Uri(serviceUri, UriKind.Absolute)); paras[0] = binding; paras[1] = address; ConstructorInfo constructor = null; try { Type[] types = new Type[2]; types[0] = typeof(System.ServiceModel.Channels.Binding); types[1] = typeof(System.ServiceModel.EndpointAddress); constructor = typeof(T).GetConstructor(types); } catch (Exception) { return null; } if (constructor != null) _instance = (T)constructor.Invoke(paras); return _instance; } /// <summary> /// Get service address /// </summary> /// <param name="serviceType"></param> /// <returns></returns> public static string GetServiceAddress(ServiceType serviceType) { return "http://localhost:7354/SLEnalbedWcfService1.svc";//get from app.config is also ok } } }