在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
动态编译与WS服务,有关系么?今天就乱弹一番,如何使用动态编译动态生成WS服务调用的代理类,然后通过这个代理类调用WS服务。 复制代码 代码如下: CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); CompilerParameters codeParameters = new CompilerParameters(); codeParameters.GenerateExecutable = false; //编译为dll,如果为true则编译为exe codeParameters.GenerateInMemory = true; //编译后的程序集保存到内存中 StringBuilder code = new StringBuilder(); //此处构造源代码 CompilerResults results = provider.CompileAssemblyFromSource(codeParameters, code.ToString()); Assembly assembly = null; //动态编译生成的程序集 if (!results.Errors.HasErrors) { assembly = results.CompiledAssembly; } 获得assembly后,随后我们即可以通过反射获取程序集里面的类型,然后实例化,调用类型方法… 不过在此之前,我们得构造WS服务的代理类,它是什么样子的呢?我们使用WCF框架,创建服务代理类也是十分简单的,常见的代理类结构如下: 服务调用代理类 复制代码 代码如下: [ServiceContract(Namespace="https://www.ogeek.net/")] public interface TestService { [OperationContract(Action = "https://www.ogeek.net/HelloWorld", ReplyAction = "https://www.ogeek.net/HelloWorldResponse")] string HelloWorld(); } public class TestServiceClient : ClientBase<TestService>, TestService { public TestServiceClient(Binding binding, EndpointAddress address) : base(binding, address) { } public string HelloWorld() { return base.Channel.HelloWorld(); } } 所以,我们要动态构造出代理类源码,应该知道服务的命名空间、服务方法的Action地址、ReplyAction地址,当然还有服务方法的名称,返回类型,参数列表。这里,我们省略掉服务方法的参数列表,构造代理类,实际上就是一个字符串组装的问题,先创建一个类型,用于保存构造代理类所要用到的参数: 服务代理类构造参数 复制代码 代码如下: public class WebServiceParamaters { public string address; public string Address { get { return address; } set { address = value; } } private string serviceNamespace; public string ServiceNamespace { get { return serviceNamespace; } set { serviceNamespace = value; } } private string methodAction; public string MethodAction { get { return methodAction; } set { methodAction = value; } } private string methodReplyAction; public string MethodReplyAction { get { return methodReplyAction; } set { methodReplyAction = value; } } private string methodName; public string MethodName { get { return methodName; } set { methodName = value; } } private string returnType; public string ReturnType { get { return returnType; } set { returnType = value; } } } 好,现在我们只需要构造出代理类源码,然后动态编译出代理类的程序集,最后通过反射调用服务方法: WebServiceProxyCreator 复制代码 代码如下: public class WebServiceProxyCreator { public Object WebServiceCaller(WebServiceParamaters parameters) { CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); CompilerParameters codeParameters = new CompilerParameters(); codeParameters.GenerateExecutable = false; codeParameters.GenerateInMemory = true; StringBuilder code = new StringBuilder(); CreateProxyCode(code, parameters); codeParameters.ReferencedAssemblies.Add("System.dll"); codeParameters.ReferencedAssemblies.Add("System.ServiceModel.dll"); CompilerResults results = provider.CompileAssemblyFromSource(codeParameters, code.ToString()); Assembly assembly = null; if (!results.Errors.HasErrors) { assembly = results.CompiledAssembly; } Type clientType = assembly.GetType("RuntimeServiceClient"); ConstructorInfo ci = clientType.GetConstructor(new Type[] { typeof(Binding), typeof(EndpointAddress) }); BasicHttpBinding binding = new BasicHttpBinding(); //只演示传统的WebService调用 EndpointAddress address = new EndpointAddress(parameters.address); Object client = ci.Invoke(new object[] { binding, address }); MethodInfo mi = clientType.GetMethod(parameters.MethodName); Object result = mi.Invoke(client, null); mi = clientType.GetMethod("Close"); //关闭代理 mi.Invoke(client, null); return result; } public static void CreateProxyCode(StringBuilder code, WebServiceParamaters parameters) { code.AppendLine("using System;"); code.AppendLine("using System.ServiceModel;"); code.AppendLine("using System.ServiceModel.Channels;"); code.Append(@"[ServiceContract("); if (!String.IsNullOrEmpty(parameters.ServiceNamespace)) { code.Append("Namespace=\"").Append(parameters.ServiceNamespace).Append("\""); } code.AppendLine(")]"); code.AppendLine("public interface IRuntimeService"); code.AppendLine("{"); code.Append("[OperationContract("); if (!String.IsNullOrEmpty(parameters.MethodAction)) { code.Append("Action=\"").Append(parameters.MethodAction).Append("\""); if (!String.IsNullOrEmpty(parameters.MethodReplyAction)) { code.Append(", "); } } if (!String.IsNullOrEmpty(parameters.MethodReplyAction)) { code.Append("ReplyAction=\"").Append(parameters.MethodReplyAction).Append("\""); } code.AppendLine(")]"); code.Append(parameters.ReturnType).Append(" "); code.Append(parameters.MethodName).AppendLine("();"); code.AppendLine("}"); code.AppendLine(); code.AppendLine("public class RuntimeServiceClient : ClientBase<IRuntimeService>, IRuntimeService"); code.AppendLine("{"); code.AppendLine("public RuntimeServiceClient(Binding binding, EndpointAddress address) :base(binding, address)"); code.AppendLine("{"); code.AppendLine("}"); code.Append("public ").Append(parameters.ReturnType).Append(" "); code.Append(parameters.MethodName).AppendLine("()"); code.AppendLine("{"); code.Append("return base.Channel.").Append(parameters.MethodName).AppendLine("();"); code.AppendLine("}"); code.AppendLine("}"); } } 注意,红色部分,由于代理类使用了WCF框架,所以编译时我们需要添加System.ServiceModel的引用,当然System.dll肯定是必须的,这里要注意,System.ServiceModel.dll应该保存到应用程序目录,否则动态编译时会引发异常,很简单,在工程引用中添加System.ServiceModel的引用,然后在属性中将拷贝到本地属性设置为true。 到此,我们就可以直接通过传入的服务地址、服务方法名称以及相关的命名空间,即可调用服务(尽管我们只能调用无参服务,并且尽管我们也只能调用使用BasicHttpBinding绑定的服务,这些限制的原因是…我懒,好吧,相信只要经过一点改动即可去掉这些限制)。 可惜,我们的程序还很傻:每次调用服务都需要去生成代码、编译、创建代理实例最后再调用,嗯…那就缓存吧: 在WebServiceParameters类中重写GetHashCode方法: 复制代码 代码如下: public override int GetHashCode() { return String.Concat(serviceNamespace, methodAction, methodReplyAction, methodName, returnType).GetHashCode(); } 然后在WebServiceProxyCreator中加入缓存机制: 复制代码 代码如下: public class WebServiceProxyCreator { private static Dictionary<int, Type> proxyTypeCatch = new Dictionary<int, Type>(); public Object WebServiceCaller(WebServiceParamaters parameters) { int key = parameters.GetHashCode(); Type clientType = null; if (proxyTypeCatch.ContainsKey(key)) { clientType = proxyTypeCatch[key]; Debug.WriteLine("使用缓存"); } else { CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); CompilerParameters codeParameters = new CompilerParameters(); codeParameters.GenerateExecutable = false; codeParameters.GenerateInMemory = true; StringBuilder code = new StringBuilder(); CreateProxyCode(code, parameters); codeParameters.ReferencedAssemblies.Add("System.dll"); codeParameters.ReferencedAssemblies.Add("System.ServiceModel.dll"); CompilerResults results = provider.CompileAssemblyFromSource(codeParameters, code.ToString()); Assembly assembly = null; if (!results.Errors.HasErrors) { assembly = results.CompiledAssembly; } clientType = assembly.GetType("RuntimeServiceClient"); proxyTypeCatch.Add(key, clientType); } ConstructorInfo ci = clientType.GetConstructor(new Type[] { typeof(Binding), typeof(EndpointAddress) }); BasicHttpBinding binding = new BasicHttpBinding(); //只演示传统的WebService调用 EndpointAddress address = new EndpointAddress(parameters.address); Object client = ci.Invoke(new object[] { binding, address }); MethodInfo mi = clientType.GetMethod(parameters.MethodName); Object result = mi.Invoke(client, null); mi = clientType.GetMethod("Close"); //关闭代理 mi.Invoke(client, null); return result; } } |
请发表评论