您的位置:首页 > 产品设计 > UI/UE

ERP 高级查询(Advanced Query)设计与实现 SQL语句解析成LLBL Gen ORM代码

2013-06-07 10:39 1191 查看

ERP高级查询(AdvancedQuery)设计与实现SQL语句解析成LLBLGenORM代码

对于开始接触基于ORM技术开发的ERP程序,在相当长的时间内还是会考虑SQL语句,而不是ORM查询。即便是在很熟悉ORM查询,也不如对SQL语句的了解程度。于是想做出一个查询工具,把SQL语句转化为C#代码,用于查询。

这样的程序片段在很多地方都需用用到。

比如SQL语句

SELECTRECNUM,CCY,DESCRIPTION,SUSPENDED,DEFAULT_RATEFROM[Currency]

查询当前的货币及其名称,默认汇率。打开高级查询功能,把这几个字段拖动到SQL语句窗口中,点击按钮Execute即可在结果窗格中看到生成的ORM语句片段。





生成的LLBLGenPro代码片段

ICurrencyManagercurrencyManager=ClientProxyFactory.CreateProxyInstance<ICurrencyManager>();
ExcludeIncludeFieldsListfieldlist=newExcludeIncludeFieldsList(false);
fieldlist.Add(CurrencyFields.RECNUM);
fieldlist.Add(CurrencyFields.CCY);
fieldlist.Add(CurrencyFields.DESCRIPTION);
fieldlist.Add(CurrencyFields.SUSPENDED);
fieldlist.Add(CurrencyFields.DEFAULT_RATE);
CurrencyEntitycurrency=currencyManager.GetCurrency(this.,null,fieldlist);

再来分析一下,如何实现这个过程。

1树结点多选功能

如上图中所示,我选中树节点Currency中的多个字段,然后把它拖动到SQL语句窗口中,自动生成SQL查询语句。这里要实现树节点多选功能。WinForms内置的树控件不支持此功能,需要另找控件。

这里,我选用CodeProject上的控件MultiSelectTreeView,它的功能用法如下介绍所示

SummarydescriptionforMultiSelectTreeView.
TheMultiSelectTreeViewinheritsfromSystem.Windows.Forms.TreeViewtoallowusertoselectmultiplenodes.Theunderlyingcomctl32TreeViewdoesn'tsupportmultipleselection.HencethisMultiSelectTreeViewlistensfortheBeforeSelect&&AfterSelect
eventstodynamicallychangetheBackColoroftheindividualtreenodesto
denoteselection.ItthenaddstheTreeNodetotheinternalarraylistofcurrentlyselectedNodesaftervalidationchecks.

TheMultiSelectTreeViewsupports
1)Select+ControlwilladdthecurrentnodetolistofSelectedNodes
2)Select+Shitftwilladdthecurrentnodeandallthenodesbetweenthetwo
(ifthestartnodeandendnodeisatthesamelevel)
3)Control+AwhentheMultiSelectTreeViewhasfocuswillselectallNodes.

2鼠标拖动编程

树结点中设置AllowDrag,加入事件响应方法ItemDrag

privatevoidtreeTables_ItemDrag(objectsender,ItemDragEventArgse)
{
if(e.Button==MouseButtons.Left)
{
DoDragDrop(e.Item,DragDropEffects.Copy);
}
}

要拖进的SQLTextEdtor,则对它加入事件响应方法

privatevoidtxtSqlScript_DragDrop(objectsender,DragEventArgse)
{
TreeNodedraggedNode=(TreeNode)e.Data.GetData(typeof(TreeNode));
if(draggedNode.Tag=="Column")
{
List<string>columns=newList<string>();
foreach(TreeNodenodeintreeTables.SelectedNodes)
{
columns.Add(node.Text.Substring(0,node.Text.IndexOf("(")));
}
stringtableName=draggedNode.Parent.Text;
stringsql=string.Format("SELECT{0}FROM[{1}]",string.Join(",",columns),tableName);
txtSqlScript.InsertText(sql);
}
elseif(draggedNode.Tag=="Table")
{
txtSqlScript.InsertText(string.Format("[{0}]",draggedNode.Text));
}
}


这几句话,根据节点代表的含义(字段,表名),来构造SQL查询语句。

3SQL语句解析

VisualStudioDatabaseEdition提供了SQL语句解析功能,引用这两个程序集,实现类似的代码





publicstaticstringExecute(QueryDataSourcedataSource,stringsql)
{
stringcsharpCode=string.Empty;
IList<ParseError>Errors;
varparser=newTSql100Parser(false);
StringReaderreader=newStringReader(sql);
IScriptFragmentresult=parser.Parse(reader,outErrors);
varScript=resultasTSqlScript;
foreach(vartsinScript.Batches)
{
foreach(varstints.Statements)
{
IterateStatement(st,refcsharpCode,dataSource);
}
}
returncsharpCode;
}

此方法根据传入的SQL语句,返回C#代码。LLBLGen是一项ORM技术,ORM首要解决的问题是C#代码如何转化为对数据库操作的SQL语句。在这里,我把这个过程反过来,根据SQL语句,得到C#代码。

数据表Currency对应的程序中的实体类型是CurrencyEntity,它的字段RECNUM对应于C#实体类型CurrencyEntity中的Recnum,这个映射关系存储于生成的C#代码中。

///<summary>InitsCurrencyEntity'smappings</summary>
privatevoidInitCurrencyEntityMappings()
{
this.AddElementMapping("CurrencyEntity","MIS",@"dbo","Currency",29);
this.AddElementFieldMapping("CurrencyEntity","AcctApForex","ACCT_AP_FOREX",true,"NVarChar",30,0,0,false,"",null,typeof(System.String),0);
this.AddElementFieldMapping("CurrencyEntity","AcctArForex","ACCT_AR_FOREX",true,"NVarChar",30,0,0,false,"",null,typeof(System.String),1);
this.AddElementFieldMapping("CurrencyEntity","ApInvoBal","AP_INVO_BAL",true,"Decimal",0,2,16,false,"",null,typeof(System.Decimal),2);
this.AddElementFieldMapping("CurrencyEntity","ApLinvoBal","AP_LINVO_BAL",true,"Decimal",0,2,16,false,"",null,typeof(System.Decimal),3);
this.AddElementFieldMapping("CurrencyEntity","ApLnetBal","AP_LNET_BAL",true,"Decimal",0,2,16,false,"",null,typeof(System.Decimal),4);
this.AddElementFieldMapping("CurrencyEntity","ApLopenBal","AP_LOPEN_BAL",true,"Decimal",0,2,16,false,"",null,typeof(System.Decimal),5);
this.AddElementFieldMapping("CurrencyEntity","ApNetBal","AP_NET_BAL",true,"Decimal",0,2,16,false,"",null,typeof(System.Decimal),6);
}

运行时,只需要调用此方法即可得到它们的映射关系,以实现将SQL语句的object(table,column)转化为C#程序对应的object(Entity,property)。

4Debug功能的实现

为了实现即使运行效果,将上面的生成的SQL语句,编译成C#程序集,再执行程序集,得到返回的结果。

CodeDomProvidercodeProvider=null;
if(Language==LanguageType.CSharp)
codeProvider=newCSharpCodeProvider();
elseif(Language==LanguageType.VB)
codeProvider=newVBCodeProvider();

//createthelanguagespecificcodecompiler
ICodeCompilercompiler=codeProvider.CreateCompiler();

//addcompilerparameters
CompilerParameterscompilerParams=newCompilerParameters();
compilerParams.CompilerOptions="/target:library";//youcanadd/optimize
compilerParams.GenerateExecutable=false;
compilerParams.GenerateInMemory=true;
compilerParams.IncludeDebugInformation=false;

//addsomebasicreferences
compilerParams.ReferencedAssemblies.Add("mscorlib.dll");
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.ReferencedAssemblies.Add("System.Data.dll");
compilerParams.ReferencedAssemblies.Add("System.Drawing.dll");
compilerParams.ReferencedAssemblies.Add("System.Xml.dll");
compilerParams.ReferencedAssemblies.Add("System.Windows.Forms.dll");


如果团队中允许多语言并行开发,此功能也可以实现将生成的C#代码,转化为VB代码。

在这里,需要构造一个Main方法,把生成的代码嵌入到这个Main方法中去,返回一个对象,把这个对象反射到Debug的网格窗口中。反射调用的入口方法如下所示

privateobjectCallEntry(Assemblyassembly,stringentryPoint)
{
objectresult=null;
try
{
//UsereflectiontocallthestaticMainfunction
Module[]mods=assembly.GetModules(false);
Type[]types=mods[0].GetTypes();
foreach(Typetypeintypes)
{
MethodInfomi=type.GetMethod(entryPoint,
BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static);
if(mi!=null)
{
if(mi.GetParameters().Length==1)
{
if(mi.GetParameters()[0].ParameterType.IsArray)
{
string[]par=newstring[1];//ifMainhasstring[]arguments
result=mi.Invoke(null,par);
}
}
else
{
result=mi.Invoke(null,null);
}
}
}
LogErrMsgs("Enginecouldnotfindthepublicstatic"+entryPoint);
}
catch(Exceptionex)
{
LogErrMsgs("Error:Anexceptionoccurred",ex);
}
returnresult;
}


最后一步,绑定结果到网格中

voidBindEntity2Grid(IEntity2entity)
{
grid.RowHeadersVisible=false;
grid.AutoGenerateColumns=false;
grid.Columns.Clear();

Typetype=entity.GetType();
PropertyInfo[]propertyInfos=type.GetProperties();
for(inti=0;i<propertyInfos.Length;i++)
{
PropertyInfoproperty=propertyInfos[i];
if(excludeColumns.Contains(property.Name))
continue;

DataGridViewTextBoxColumncolumn=newDataGridViewTextBoxColumn();
column.HeaderText=property.Name;
column.DataPropertyName=property.Name;
column.Name=property.Name;
grid.Columns.Add(column);
}
for(inti=0;i<propertyInfos.Length;i++)
{
PropertyInfoproperty=propertyInfos[i];
if(excludeColumns.Contains(property.Name))
continue;

grid.Rows[0].Cells[property.Name].Value=ReflectionHelper.GetPropertyValue(entity,property.Name);
}
}


这里的目的是读取对象的属性,生成Grid的列,并填充值。

这里使用的SQL语法高亮控件来自于CodeProject的FastColoredTextBox。读取一个数据库中所有的表及表的字段用到如下的SQL语句,供您参考。

SELECTnameFROMsysobjectsWHERExtype='U'

SELECTsyscolumns.NAMEAS[ColumnName],systypes.NAMEAS[ColumnType],syscolumns.lengthAS[ColumnLength],syscolumns.isnullableAS[Nullable]FROMsyscolumns
leftjoinsystypesonsyscolumns.xusertype=systypes.xusertype
WHEREid=(selectidfromsysobjectswherename='Currency‘


此功能有个明显的Bug,没有把功能编码显示在地址栏中,因为它继承于FormBase,需要改成FunctionFormBase可达到目的,界面可能需要重新排版一下,改变继承的基类会让窗体设计器重新加载基类控件,子类的控件会丢失。

[FunctionCode("SAUMAQ")]
publicpartialclassAdvancedQuery:FormBase
{
privatestring_fileName;

publicAdvancedQuery()
{
InitializeComponent();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐