SQL 和 VB.NET 代码生成器






3.72/5 (31投票s)
2005年2月22日
5分钟阅读

223325

10410
一篇关于生成 SQL 存储过程和 VB.NET 函数调用的文章。
引言
我遇到过许多项目,它们要么生成 VB/C# 代码,要么生成 SQL 代码。这个应用程序可以同时生成表的 SQL 存储过程和调用这些存储过程的 VB.NET 函数模块。这是一个非常有用的工具,可以生成所需的代码,尤其是在将 ASP/VB 代码转换为 ASP.NET/VB.NET 时。此外,该应用程序还提供了选择字段以生成存储过程的选项。
背景
我在一个使用 Microsoft Application Blocks for .NET 访问 SQL Server 2000 数据库的项目中开发了这部分核心代码。我们有封装数据访问的类,以及为订单、客户等每个业务对象设计的实体类。
我们的想法是创建一个工具,用于辅助生成用于操作数据库的存储过程、函数和实体。例如,为了创建一个订单,我们需要在 *order* 表中插入一行,并在 *order detail* 表中插入一个或多行。可能还涉及其他表,例如特殊说明、送货地址(可能不止一个送货点)。为此,将有一个或多个插入行的存储过程,这些存储过程通过 VB.NET 数据访问类执行。这个类将封装添加、删除、修改订单所需的所有功能(数据访问逻辑),这无非是在 Order 和 OrderDetails(以及其他)表中插入、删除和更新行。用于插入订单的 VB.NET 函数将接收一个“Order
”对象作为参数,并准备调用存储过程(例如,usp_InsertOrder
)所需的 SQL 命令参数,以便将行插入 Order、OrderDetails 和其他表中。
使用代码
在第一步中,用户会看到一个网络中可用 SQL 服务器的列表,这些服务器通过调用 NetServerEnum API 填充到 ComboBox
中。此 API 可用于获取其他服务器(如域控制器等)的列表。我已经修改了代码以适应目的,并创建了一个新类,以便它可以在任何其他项目中复用。
当在提供服务器登录用户名和密码后单击“连接”按钮时,将显示所选服务器中可用的数据库。根据需要显示的内容选择“表”或“存储过程”(单选按钮)。当选择“表”选项时,您可以选择创建实体类、插入、删除或更新存储过程。当选择“表”选项时,DataGrid
dgColumns
将用表字段填充;当选择“存储过程”选项时,将用存储过程参数填充。
生成实体类
要创建实体类,将使用所选表的所有列。每列都将以“_”为前缀,成为私有变量,并创建一个公共属性。代码非常简单,只需遍历表行并为每个表行生成属性。
strBuilder.Append("Public class " & strEntityName & vbCrLf)
strBuilder.Append("#Region " & Chr(34) & _
" Private variables " & Chr(34) & vbCrLf)
'Build Private variables list based
'on the table column names and data type
For i = 0 To dsColumns.Tables("Tables").Rows.Count - 1
strBuilder.Append("Private _" & _
dsColumns.Tables("Tables").Rows(i).Item("COLUMN_NAME"))
strBuilder.Append(" as " & _
GetVBDataType(dsColumns.Tables("Tables").Rows(i).Item("TYPE_NAME"))_
& vbCrLf)
Next
strBuilder.Append(vbCrLf)
strBuilder.Append("# end Region " & _
vbCrLf) ' Provate variable region ends
'Build Propertis
strBuilder.Append("#Region " & Chr(34) _
& " Properties " & Chr(34) & vbCrLf)
For i = 0 To dsColumns.Tables("Tables").Rows.Count - 1
strBuilder.Append("Public Property ")
dbType = GetVBDataType(dsColumns.Tables("Tables").Rows(i).Item("TYPE_NAME"))
strColumnName = dsColumns.Tables("Tables").Rows(i).Item("COLUMN_NAME")
strBuilder.Append(strColumnName & "() As " & dbType & vbCrLf)
strBuilder.Append("Get" & vbCrLf)
strBuilder.Append("return _" & strColumnName & vbCrLf)
strBuilder.Append("end Get" & vbCrLf)
strBuilder.Append("Set(ByVal Value As " & dbType & ")" & vbCrLf)
strBuilder.Append("_" & strColumnName & " = Value" & vbCrLf)
strBuilder.Append("end set" & vbCrLf)
strBuilder.Append("end Property ")
strBuilder.Append(vbCrLf)
strBuilder.Append(vbCrLf)
Next
strBuilder.Append(vbCrLf)
strBuilder.Append("# end Region " & vbCrLf) 'Properties region ends
strBuilder.Append(vbCrLf & "end class") ' Class building Ends
strFunctionText.Append(strBuilder.ToString)
strBuilder = Nothing
生成的类将为所选表的每个字段都有一个对应的属性。只需进行简单的修改,就可以迭代 dgColumns
DataGrid
来生成类代码,该代码将仅为 DataGrid
中选定的字段提供属性。
生成存储过程
插入行时,必须为每个不允许 null 值的列提供值。因此,在 DataGrid
dgColumns
中,所有“允许 Null”列值为“否”的行都将默认选中,并且无法取消选中。如果需要新列,可以选择它们(只需使用 Ctrl/Shift 键组合来选择一行或多行)。
在生成“更新”存储过程时,主键将用在 where
子句中,其他选定列将被视为需要更新的列,并生成相应的参数。在 dgColumns
的主键列中,行无法取消选中,但其他列可以选择。
'Adding where condition using Primary keys
'by default all the primary keys are used in where condition
strBld.Append(Space(7) & "WHERE" & vbCrLf)
For IntJ = 0 To dsColumns.Tables("Pkeys").Rows.Count - 2
strColumnName = _
dsColumns.Tables("Pkeys").Rows(IntJ).Item("COLUMN_NAME")
strBld.Append(Space(10) & strColumnName _
& " = " & "@" & strColumnName _
& " and " & vbCrLf)
Next
strColumnName = _
dsColumns.Tables("Pkeys").Rows(IntJ).Item("COLUMN_NAME")
strBld.Append(Space(10) & strColumnName _
& " = " & "@" & strColumnName & vbCrLf)
在生成删除存储过程时,dgColumns
中选定的所有行都将用在 where
子句中。在这种情况下,主键列行也预先选中且无法取消选中,但可以选中更多行。
For intI = 0 To intGridRowCount - 1
If dgDetails.IsSelected(intI) Then
If intI <> 0 Then strBld.Append(" and " & vbCrLf)
strColumnName = dgDetails.Item(intI, 1)
strBld.Append(Space(10) & strColumnName _
& " = " & "@" & strColumnName)
End If
Next
生成函数调用
如果通过选择适当的单选按钮将存储过程填充到组合框中,DataGrid
将显示执行存储过程所需的参数。单击“生成函数”按钮时,将生成 VB.NET 函数代码,其中包含准备参数的代码,但不包括为准备好的参数提供值的代码行。
生成函数代码遵循以下步骤:
- 声明 SQL 参数对象数组。
- 启动
Try
块。 - 准备每个参数。
- 调用 Microsoft Application Blocks 的
SQLHelper
类的ExecuteDataset
函数。 - 实现
Finally
块以销毁所有对象。 - 关闭
Try
块。
Private Sub GenerateFunction()
Dim strBuilder As New StringBuilder
Dim strFunctionName As String
Dim i As Integer
btnSaveToFile.Enabled = True
strBuilder.Append("Public function ")
strFunctionName = InputBox("Enter Function Name", "Function Name")
'Return type assumed to be Dataset
strBuilder.Append(strFunctionName & "() _
as System.Data.Dataset") 'Modify this statement to add return type
strBuilder.Append(vbCrLf)
'Declaring Dataset varaibale
strBuilder.Append("Dim dsResults as System.Data.DataSet" & vbCrLf)
'Delacring Parameter length based on the number of parameters required
strBuilder.Append("Dim SQLParam(") ' Creates SQLParameter object array
i = dsColumns.Tables("Procedure").Rows.Count - 1
If i > 1 Then
strBuilder.Append(i - 1 & ") as SQLParameter")
Else
strBuilder.Append(0 & ") as SQLParameter")
End If
strBuilder.Append(vbCrLf)
strBuilder.Append("Try") ' Try block starts
strBuilder.Append(vbCrLf & vbCrLf)
For i = 1 To dsColumns.Tables("Procedure").Rows.Count - 1
'strBuilder.Append("ErrMsg =" & Chr(34) & " _
Preparing Parameter " & i & Chr(34))
strBuilder.Append(vbCrLf)
strBuilder.Append("SQLParam(" & _
(i - 1).ToString & _
")=new System.Data.SqlClient.SqlParameter" _
& vbCrLf)
strBuilder.Append("with SQLParam(" & _
(i - 1).ToString & ")" & vbCrLf)
'Begin of WITH block
strBuilder.Append(".ParameterName=" & _
Chr(34) & _
dsColumns.Tables("Procedure").Rows(i).Item("COLUMN_NAME") _
& Chr(34) & vbCrLf)
strBuilder.Append(".DbType=" & _
GetSQLDataType(dsColumns.Tables("Procedure").Rows(i).Item("TYPE_NAME"))_
& vbCrLf)
If dsColumns.Tables("Procedure").Rows(i).Item("COLUMN_TYPE")_
= 1 Then 'Input parameter
strBuilder.Append(".Direction = _
ParameterDirection.Input" & vbCrLf)
ElseIf _
dsColumns.Tables("Procedure").Rows(i).Item("COLUMN_TYPE")_
= 2 Then ' Output parameter
strBuilder.Append(".Direction = ParameterDirection.Output" & vbCrLf)
End If
If Not _
IsDBNull(dsColumns.Tables("Procedure").Rows(i).Item("CHAR_OCTET_LENGTH"))_
Then
'This will be null for all numeric data types
strBuilder.Append(".Size = " & _
dsColumns.Tables("Procedure").Rows(i).Item("PRECISION")_
& vbCrLf)
End If
strBuilder.Append("'.Value = " & _
"'Uncomment this after" & _
" providing value for this parameter" & vbCrLf)
strBuilder.Append("End with" & vbCrLf)
' End of With block
strBuilder.Append(vbCrLf & vbCrLf)
Next
'Write ExecuteDataset statment
'Replace this statement if required with any other
'For example you may not be using
'MS Data Access Blocks, or ExecuteScalar etc...
strBuilder.Append("'calling ExecuteDataset Method " & vbCrLf)
strBuilder.Append("SqlHelper.ExecuteDataset" & _
"(m_ConnectionString, CommandType.StoredProcedure,"_
& Chr(34))
strBuilder.Append(cmbDetails.SelectedValue.ToString()_
& Chr(34) & ",")
strBuilder.Append("SQLParam)")
strBuilder.Append(vbCrLf & vbCrLf)
'Catch block
strBuilder.Append("Catch ex As Exception")
strBuilder.Append(vbCrLf & vbCrLf)
'
'Implement you own method of exception handling
strBuilder.Append("'Your own method of exception handling" & vbCrLf)
'like sending an email to the concerned
'
strBuilder.Append(vbCrLf & vbCrLf)
strBuilder.Append("Finally")
strBuilder.Append(vbCrLf & vbCrLf)
'Destroying SQL Parameter Objects
strBuilder.Append("Dim i As Integer" & vbCrLf)
strBuilder.Append("For i = 0 To SQLParam.Length - 1" & vbCrLf)
strBuilder.Append("SQLParam(i) = Nothing" & vbCrLf)
strBuilder.Append("Next" & vbCrLf)
strBuilder.Append(vbCrLf)
strBuilder.Append("end try" & vbCrLf) ' End of Try block
strBuilder.Append(vbCrLf)
'Return Statement
strBuilder.Append("Return dsResults")
' Normally a dataset, change this statement if required
strBuilder.Append(vbCrLf & vbCrLf)
strBuilder.Append("End function") 'End of function
strFunctionText.Append(strBuilder.ToString)
strBuilder = Nothing
End Sub
仅会生成与异常处理相关的骨架代码,因为不同项目使用不同的技术;例如,异常详细信息可以发送到相关人员的电子邮件或记录在数据库中等。因此,请修改此代码以在“Catch
”和“End Try
”语句之间添加代码。
温馨提示:如前所述,在生成的代码中,为 SQL 参数赋值的语句将被注释掉。下面是生成注释掉的 VB.NET 代码行,需要取消注释,并在使用生成的函数之前由用户为参数分配适当的值。
strBuilder.Append("'.Value = " & _
"'Uncomment this after providing value for this parameter" & vbCrLf)
单击“保存到文件”按钮将生成的类、存储过程或函数保存到文件中。
关注点
编写调用存储过程的函数或过程可能很烦人,因为参数名称和顺序必须与存储过程中声明的完全相同。而在编写操作数据库表的存储过程时,每个“not null
”列都需要参数。使用此工具可以减轻这种痛苦。
此外,生成实体类并为每个表字段声明属性也可能很累人。我发现这个工具非常方便,可以快速生成大部分所需的代码,且在实际使用前需要很少甚至无需修改。
历史
首次发布。