.NET的动态编译与WS服务调用详解_asp.net教程-查字典教程网
.NET的动态编译与WS服务调用详解
.NET的动态编译与WS服务调用详解
发布时间:2016-12-29 来源:查字典编辑
摘要:动态编译与WS服务,有关系么?今天就乱弹一番,如何使用动态编译动态生成WS服务调用的代理类,然后通过这个代理类调用WS服务。首先,动态编译这...

动态编译与WS服务,有关系么?今天就乱弹一番,如何使用动态编译动态生成WS服务调用的代理类,然后通过这个代理类调用WS服务。

首先,动态编译这玩意在.NET里面是非常简单的,实际上只涉及到两个类型:CodeDomProvider以及CompilerParameters他们都位于System.CodeDom.Compiler命名空间。

以下代码可将源码动态编译为一个程序集:

动态编译

复制代码 代码如下:

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="http://www.jb51.net/")]

public interface TestService

{

[OperationContract(Action = "http://www.jb51.net/HelloWorld", ReplyAction = "http://www.jb51.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;

}

}

相关阅读
推荐文章
猜你喜欢
附近的人在看
推荐阅读
拓展阅读
  • 大家都在看
  • 小编推荐
  • 猜你喜欢
  • 最新asp.net教程学习
    热门asp.net教程学习
    编程开发子分类