/****************************************************************\
*
* 用 system.reflection.emit 来自动生成调用储存过程的实现!
*
* by http://lostinet.com
*
* copyrights : not-reversed
*
\****************************************************************/
//使用的例子
namespace lostinet.sample
{
using system;
using system.data;
using system.data.sqlclient;
using system.windows.forms;
//定义一个接口,用于定义存储过程
interface inorthwindstoredprocedures
{
//定义存储过程对应的方法
dataset custorderhist(string customerid);
//如果储存过程名字和方法名字不同,应该用sqlaccessattribute来进行说明
[sqlaccess("employee sales by country")]
datatable employeesalesbycountry(datetime beginning_date,datetime ending_date);
//…more…
//more ideas..
//直接执行sql语句?
//[sqlaccess(sqlaccesstype.sqlquery,"select * from employees where employeeid=@empid")]
//datatable selectemployee(int empid);
}
class consoleapplication
{
[stathread]
static void main(string[] args)
{
using(sqlconnection conn=new sqlconnection("server=(local);trusted_connection=true;database=northwind"))
{
//一句话就把实现创建了!
//需要传如 sqlconnection 和 sqltransaction
//sqltransaction可以为null
//这个好就好在,只要能得到sqlconnection/sqltransaction就能用这个方法了,所以兼容 lostinet.data.sqlscope
inorthwindstoredprocedures nsp=(inorthwindstoredprocedures)
storedprocedure.createstoredprocedureinterface(typeof(inorthwindstoredprocedures),conn,null);
//调用储存过程并且显示
showdata("custorderhist alfki",nsp.custorderhist("alfki"));
showdata("employee sales by country",nsp.employeesalesbycountry(new datetime(1998,1,1),new datetime(1999,1,1)));
}
}
static void showdata(string title,object data)
{
form f=new form();
f.width=600;
f.height=480;
f.text=title;
datagrid grid=new datagrid();
grid.dock=dockstyle.fill;
grid.datasource=data;
f.controls.add(grid);
f.showdialog();
}
}
}
#region //实现方法(不完整)
namespace lostinet.sample
{
using system;
using system.collections;
using system.reflection;
using system.reflection.emit;
using system.data;
using system.data.sqlclient;
//这个类作为实现的基类,
//目的是提供储存 sqlconnection/sqltransaction 和公用的一些方法
//这个类必须为public,否则无法继承
//但开发者不会显式访问这个类
public class spinterfacebase : idisposable
{
public spinterfacebase()
{
}
public void dispose()
{
}
//createstoredprocedureinterface会把相关的值sqlconnection/sqltransaction存到这里
public sqlconnection connection;
public sqltransaction transaction;
//创建一个sqlcommand
public sqlcommand createcommand(string spname)
{
sqlcommand cmd=new sqlcommand(spname,connection,transaction);
cmd.commandtype=commandtype.storedprocedure;
//todo:
//cmd.parameters.add("@returnvalue",…
return cmd;
}
//由 type 推算出 sqldbtype , 未完成
sqldbtype getsqldbtype(type type)
{
//todo:switch(type)…
return sqldbtype.nvarchar;
}
//定义参数
public void defineparameter(sqlcommand cmd,string name,type type,parameterdirection direction)
{
sqlparameter param=new sqlparameter("@"+name,getsqldbtype(type));
param.direction=direction;
cmd.parameters.add(param);
}
//在sqlcommand执行前设置参数值
public void setparameter(sqlcommand cmd,string name,object value)
{
cmd.parameters["@"+name].value=(value==null?dbnull.value:value);
}
//在sqlcommand执行后取得参数值
public object getparameter(sqlcommand cmd,string name)
{
return cmd.parameters["@"+name].value;
}
//根据不同的返回值执行不同的操作
public sqldatareader executedatareader(sqlcommand cmd)
{
return cmd.executereader();
}
public object executescalar(sqlcommand cmd)
{
return cmd.executescalar();
}
public void executenonquery(sqlcommand cmd)
{
cmd.executenonquery();
}
public dataset executedataset(sqlcommand cmd)
{
dataset ds=new dataset();
using(sqldataadapter sda=new sqldataadapter(cmd))
{
sda.fill(ds);
}
return ds;
}
public datatable executedatatable(sqlcommand cmd)
{
datatable table=new datatable();
using(sqldataadapter sda=new sqldataadapter(cmd))
{
sda.fill(table);
}
return table;
}
public datarow executedatarow(sqlcommand cmd)
{
datatable table=executedatatable(cmd);
if(table.rows.count==0)
return null;
return table.rows[0];
}
}
public class storedprocedure
{
static public object createstoredprocedureinterface(type interfacetype,sqlconnection connection,sqltransaction transaction)
{
//检查参数
if(interfacetype==null)throw(new argumentnullexception("interfacetype"));
if(!interfacetype.isinterface)
throw(new argumentexception("argument is not interface","interfacetype"));
if(connection==null)throw(new argumentnullexception("connection"));
if(transaction!=null)
{
if(transaction.connection!=connection)
throw(new argumentexception("transaction.connection!=connection","transaction"));
}
//创建storedprocedure
storedprocedure spemit=new storedprocedure();
spemit.interfacetype=interfacetype;
spemit.connection=connection;
spemit.transaction=transaction;
//创建
return spemit.createinstance();
}
//用于储存已创建的类型
static hashtable emittedtypes=new hashtable();
type interfacetype;
sqlconnection connection;
sqltransaction transaction;
private storedprocedure()
{
}
object createinstance()
{
lock(interfacetype)
{
//如果没有创建具体的实现,则创建它
if(emittedtype==null)
{
emittedtype=(type)emittedtypes[interfacetype];
if(emittedtype==null)
{
createtype();
//储存已创建类型
emittedtypes[interfacetype]=emittedtype;
}
}
}
//创建具体的实例
spinterfacebase spi=(spinterfacebase)activator.createinstance(emittedtype);
//设置sqlconnection/sqltransaction
spi.connection=connection;
spi.transaction=transaction;
return spi;
}
type emittedtype;
typebuilder typebuilder;
//创建类型
void createtype()
{
//创建 assembly
//assemblybuilderaccess.run-表示只用于运行,不在磁盘上保存
assemblyname an=new assemblyname();
an.name="assembly."+interfacetype.fullname+".implementation";
assemblybuilder asmbuilder=appdomain.currentdomain.definedynamicassembly(an,assemblybuilderaccess.run);
//创建module
modulebuilder mdlbuilder=asmbuilder.definedynamicmodule("module."+interfacetype.fullname+".implementation");
//创建type,该类型继承 spinterfacebase
typebuilder=mdlbuilder.definetype(interfacetype.fullname+".implementation",typeattributes.class,typeof(spinterfacebase));
//实现所有的接口方法
emitinterface(interfacetype);
//如果interfacetype是基于其他接口的
foreach(type subinterface in interfacetype.getinterfaces())
{
//idisposable不需要实现,由spinterfacebase实现了
if(subinterface==typeof(idisposable))
continue;
emitinterface(subinterface);
}
emittedtype=typebuilder.createtype();
}
void emitinterface(type type)
{
//实现接口
typebuilder.addinterfaceimplementation(type);
//列出接口的成员
foreach(memberinfo member in type.getmembers(bindingflags.instance|bindingflags.public))
{
//约定-成员必须是方法,不能有属性啊,事件之类的
if(member.membertype!=membertypes.method)
throw(new exception("could not emit "+member.membertype+" automatically!"));
//取得接口中定义的方法
methodinfo method=(methodinfo)member;
//计算新方法的属性,在原来方法的属性上复制过来,并且不是public/abstract,加上private
methodattributes methodattrs=method.attributes;
methodattrs&=~(methodattributes.public|methodattributes.abstract);
methodattrs|=methodattributes.private;
parameterinfo[] paraminfos=method.getparameters();
int paramlength=paraminfos.length;
//取得参数的类型数组
type[] paramtypes=new type[paramlength];
for(int i=0;i<paramlength;i++)
{
paramtypes[i]=paraminfos[i].parametertype;
}
//在typebuilder上建立新方法,参数类型与返回类型都与接口上的方法一致
methodbuilder mthbuilder=typebuilder.definemethod(method.name,methodattrs,method.callingconvention,method.returntype,paramtypes);
//复制新方法上的参数的名字和属性
for(int i=0;i<paramlength;i++)
{
parameterinfo pi=paraminfos[i];
//对于instance,参数position由1开始
mthbuilder.defineparameter(i+1,pi.attributes,pi.name);
}
//指定新方法是实现接口的方法的。
typebuilder.definemethodoverride(mthbuilder,method);
//在类型上定义一个字段,这个字段用于储存被方法使用的sqlcommand
fieldbuilder field_cmd=typebuilder.definefield("_cmd_"+method.name,typeof(sqlcommand),fieldattributes.private);
//ilgenerator 是用于生成实现代码的对象
ilgenerator ilg=mthbuilder.getilgenerator();
//定义临时变量
localbuilder local_res=ilg.declarelocal(typeof(object));
//定义一个用于跳转的label
label label_cmd_ready=ilg.definelabel();
//this._cmd_methodname
ilg.emit(opcodes.ldarg_0); //this
ilg.emit(opcodes.ldfld,field_cmd);//._cmd_methodname
//if(this._cmd_methodname!=null) 跳到 label_cmd_ready
ilg.emit(opcodes.brtrue,label_cmd_ready);
//如果this._cmd_methodname为null,则运行下面代码来创建sqlcommand
//this._cmd_methodname=this.createcommand("methodname");
ilg.emit(opcodes.ldarg_0);
//this.createcommand
ilg.emit(opcodes.ldarg_0);//参数0
ilg.emit(opcodes.ldstr,sqlaccessattribute.getspname(method));//参数1
//调用
ilg.emit(opcodes.callvirt,typeof(spinterfacebase).getmethod("createcommand",bindingflags.instance|bindingflags.public));
ilg.emit(opcodes.stfld,field_cmd);// ._cmd_methodname=
//this.defineparameter(…)
if(paramlength!=0)
{
//取得defineparameter的引用
methodinfo method_defineparameter=typeof(spinterfacebase).getmethod("defineparameter",bindingflags.instance|bindingflags.public);
for(int i=0;i<paramlength;i++)
{
//取得各参数
parameterinfo pi=paraminfos[i];
//this.defineparameter(this._cmd_methodname,"parametername",typeof(parametertype),parameterdirection.xxx);
//参数0 – this
ilg.emit(opcodes.ldarg_0);
//参数1 – this._cmd_methodname
ilg.emit(opcodes.ldarg_0);
ilg.emit(opcodes.ldfld,field_cmd);
//参数2 – "parametername"
ilg.emit(opcodes.ldstr,pi.name);
//参数3 – typeof(parametertype)
ilg.emit(opcodes.ldtoken,pi.parametertype);
//参数4 – parameterdirection.xxx
if(pi.parametertype.isbyref)
{
ilg.emit(opcodes.ldc_i4,(int)parameterdirection.inputoutput);
}
else if(pi.isout)
{
ilg.emit(opcodes.ldc_i4,(int)parameterdirection.output);
}
else
{
ilg.emit(opcodes.ldc_i4,(int)parameterdirection.input);
}
//调用defineparameter
ilg.emit(opcodes.callvirt,method_defineparameter);
}
}
//到这里 _cmd_commandname 已经 ok 了。
//设置label_cmd_ready就指这里
ilg.marklabel(label_cmd_ready);
//cmd!=null now.
if(paramlength!=0)
{
//现在要把方法的参数的值设置到sqlparameter上
methodinfo method_setparameter=typeof(spinterfacebase).getmethod("setparameter",bindingflags.instance|bindingflags.public);
for(int i=0;i<paramlength;i++)
{
parameterinfo pi=paraminfos[i];
//如果参数是 out 的,则不需要设置
if(!pi.parametertype.isbyref&&pi.isout)
continue;
//this.setparameter(this._cmd_methodname,"parametername",parametername);
ilg.emit(opcodes.ldarg_0);
ilg.emit(opcodes.ldarg_0);
ilg.emit(opcodes.ldfld,field_cmd);
ilg.emit(opcodes.ldstr,pi.name);
//取得参数值,如果参数为valuetype,则box到object
ilg.emit(opcodes.ldarg,i+1);
if(pi.parametertype.isvaluetype)
ilg.emit(opcodes.box,pi.parametertype);
ilg.emit(opcodes.callvirt,method_setparameter);
}
}
//现在要执行储存过程(执行sqlcommand)了
//这里根据返回值类型判断怎样执行sqlcommand
type returntype=method.returntype;
//如果是 void 的,则不需要返回值
bool nores=returntype==typeof(void);
methodinfo method_execute=null;
if(nores)
{
//不需要返回值
method_execute=typeof(spinterfacebase).getmethod("executenonquery",bindingflags.instance|bindingflags.public);
}
else if(returntype==typeof(object))
{
//返回object
method_execute=typeof(spinterfacebase).getmethod("executescalar",bindingflags.instance|bindingflags.public);
}
else if(returntype==typeof(dataset))
{
//返回dataset
method_execute=typeof(spinterfacebase).getmethod("executedataset",bindingflags.instance|bindingflags.public);
}
else if(returntype==typeof(datatable))
{
//返回datatable
method_execute=typeof(spinterfacebase).getmethod("executedatatable",bindingflags.instance|bindingflags.public);
}
else if(returntype==typeof(datarow))
{
//返回datarow
method_execute=typeof(spinterfacebase).getmethod("executedatarow",bindingflags.instance|bindingflags.public);
}
else
{
//返回其他类型
foreach(type retinterface in returntype.getinterfaces())
{
//如果是返回idatareader
if(retinterface==typeof(idatareader))
{
//只支持sqldatareader
if(!returntype.isassignablefrom(typeof(sqldatareader)))
throw(new exception("sqldatareader could not convert to "+returntype.fullname));
method_execute=typeof(spinterfacebase).getmethod("executedatareader",bindingflags.instance|bindingflags.public);
break;
}
}
}
//如果找不到适合的策略,
if(method_execute==null)
{
//todo:当然,这里应该有返回int32,string,…的,不过懒得再写了。
//抛出异常,提示不支持该返回类型,要作者改改:)
throw(new notsupportedexception("notsupport returntype:"+returntype.fullname));
}
//this.executexxx(this._cmd_methodname)
ilg.emit(opcodes.ldarg_0);
ilg.emit(opcodes.ldarg_0);
ilg.emit(opcodes.ldfld,field_cmd);
ilg.emit(opcodes.callvirt,method_execute);
//如果有返回值的,则是
//local_res=this.executexxx(this._cmd_methodname)
if(!nores)
{
if(returntype.isvaluetype)
ilg.emit(opcodes.box,returntype);
ilg.emit(opcodes.stloc,local_res);
}
if(paramlength!=0)
{
//这里处理ref/out的参数
methodinfo method_getparameter=typeof(spinterfacebase).getmethod("getparameter",bindingflags.instance|bindingflags.public);
for(int i=0;i<paramlength;i++)
{
parameterinfo pi=paraminfos[i];
//如果不是ref/out则跳过
if(!pi.parametertype.isbyref&&!pi.isout)
continue;
//parametername=this.getparameter(this._cmd_methodname,"parametername")
ilg.emit(opcodes.ldarg_0);
ilg.emit(opcodes.ldarg_0);
ilg.emit(opcodes.ldfld,field_cmd);
ilg.emit(opcodes.ldstr,pi.name);
ilg.emit(opcodes.callvirt,method_getparameter);
//如果类型是值类型,则需要 unbox
if(pi.parametertype.isvaluetype)
ilg.emit(opcodes.unbox,pi.parametertype);
ilg.emit(opcodes.starg,i+1);
}
}
//如果是 void , 则直接 return;
//否者是 return local_res , 如果返回值类型是valuetype,则需要unbox
if(!nores)
{
ilg.emit(opcodes.ldloc,local_res);
if(returntype.isvaluetype)
ilg.emit(opcodes.unbox,returntype);
}
ilg.emit(opcodes.ret);
// //throw(new notimplementedexception());
// ilg.emit(opcodes.newobj,typeof(notimplementedexception).getconstructor(new type[0]));
// ilg.emit(opcodes.throw);
}
}
}
public enum sqlaccesstype
{
storedprocedure
//todo:
//,sqlquery
}
[attributeusage(attributetargets.method)]
public class sqlaccessattribute : attribute
{
string _sp;
public sqlaccessattribute(string spname)
{
_sp=spname;
}
public string storeprocedure
{
get
{
return _sp;
}
}
static public string getspname(methodinfo method)
{
if(method==null)throw(new argumentnullexception("method"));
object[] attrs=method.getcustomattributes(typeof(sqlaccessattribute),false);
if(attrs==null||attrs.length==0)
return method.name;
return ((sqlaccessattribute)attrs[0]).storeprocedure;
}
//todo:
// public sqlaccessattribute(sqlaccesstype type,string text)
// {
//
// }
}
}
#endregion
