.NET的动态编译与WS服务调用详解
程序员文章站
2024-03-01 17:49:52
动态编译与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="//www.jb51.net/")]
public interface testservice
{
[operationcontract(action = "//www.jb51.net/helloworld", replyaction = "//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;
}
}
上一篇: css元素定位
下一篇: mysql 数据类型TIMESTAMP
推荐阅读