生成 SQL 表的类表示






4.77/5 (14投票s)
自动将 SQL 表解析为 C# 类,并包含 insert、select 等函数。

引言
本项目构建了一个程序,该程序获取给定数据源(服务器)中所有数据库的列表,以及给定数据库名称内的表列表;然后为该表生成一个类抽象层。生成的类与表同名。如果需要,可以将该类表示形式保存为 .cs 源文件。

代码解释
正如您在旁边看到的,该解决方案包含一个类库项目和一个作为启动项目的测试窗体项目。测试窗体包含两个窗体:TestMain.cs 和 Input.cs。Input 窗体获取数据源(通常是 '.' 用于受信任连接的服务器名称)。或者,它获取用于身份验证登录的服务器名称、用户 ID、密码条目。然后 TestForm
接管控制,如果输入有效,它会在屏幕上显示。否则应用程序退出。
在 sqlclassfactory
类库项目中,您可以看到不同的类。所有这些类都声明为 static
。起点是 Constants
类。此类包含不易变的连接字符串等字符串。ConnStrArgs.cs 包含一个名为 Args
的 static
类。此类至关重要。因为它是在进行其他数据库操作之前必须设置的类。所有其他 static
类都从此类获取服务器信息。如下所示,必须先设置它
SqlClassFactory.Args.Reset();
SqlClassFactory.Args.Type = SqlClassFactory.enmConnStrType.Trusted;
SqlClassFactory.Args.DataSource = dataSource;
SqlClassFactory.Args.InitialCatalog = "master";
下一个类是 Connections.cs 类。此类有一个名为 _connection
的 private
字段,类型为 SqlConnection
。它包含一个名为 GetConnection()
的 static
方法。此方法从 Constants
获取格式字符串,并从 Args
类获取参数放入该格式字符串,然后构建新连接并返回。正如您所看到的,每次调用该方法时,都会使用可能被反复设置的 Args
创建新连接。
Commands
类只是原始 SqlCommands
的宿主类。命令的 CommandText
和 Connection
属性在 Adapters
中设置。在 Adapters
类中,这些属性的设置如下
Commands.ListDBCmd.CommandText = Constants.ListDBStr;
Commands.ListDBCmd.Connection = Connections.GetConnection();
Adapters._serverDA = new SqlDataAdapter(Commands.ListDBCmd);
例如,这里设置了 ListDBCmd
命令,并将 ServerDA
属性设置为具有此命令的 new SqlDataAdapter
。
最后一个类是 Generator
类。这个 static
类包含一个名为 BeginGeneration(string)
的方法。就像构造函数一样,此方法接受一个字符串参数 rawClassText
并将其内部化。我将在“使用代码”部分进行详细解释。
“Class.txt”
Class.txt 是包含 rawClassText
的文件。rawClassText
是用于从指定数据库生成表类的 string
。Class.txt 的一些字段是特殊字段 <...\>。这些字段被替换为其匹配项,例如 propertyName
或 className
。
public DataTable DoSelect(string SqlCommandText)
{
SqlCommand selectCommand = new SqlCommand(SqlCommandText);
selectCommand.Connection = sqlConnection;
sqlDA.SelectCommand = selectCommand;
DataTable retdt = new DataTable();
sqlDA.Fill(retdt);
return retdt;
}
这里您看到“Class.txt”的 DoSelect()
方法。这里 sqlDA
是类成员,在构造函数中初始化。此方法是类中最简单的方法。它填充 retdt
并返回它。
public bool DoInsert(string SqlCommandText, params object[] args)
{
SqlCommand insertCommand = new SqlCommand(SqlCommandText);
string argInCmd = null;
Regex r = new Regex(@"@(\S+)", RegexOptions.IgnoreCase);
int i = 0;
if (r.Matches(SqlCommandText).Count == args.Length)
{
foreach (object arg in args)
{
argInCmd = r.Matches(SqlCommandText)[i].ToString();
argInCmd = argInCmd.Trim().TrimEnd(new char[] {')', ','});
insertCommand.Parameters.AddWithValue(argInCmd, arg);
i++;
}
}
else
return false;
insertCommand.Connection = sqlConnection;
sqlConnection.Open();
bool ret = insertCommand.ExecuteNonQuery() > 0;
sqlConnection.Close();
return ret;
}
这里您看到“Class.txt”的 DoInsert()
方法。此方法稍微复杂一些。它的用途是
DoInsert("Insert into Employees (firstname, lastname) values (@fname, @lname)", _
"Ozgur", "Sonmez");
这是整个项目中“最聪明”的方法。它接受一个 params 参数 args
。此参数包含要添加到 sqlcommand
的参数。在上面的示例中,argInCmd
字符串首先是 '@fname
',然后是 '@lname
'。argInCmd
恰好是 arg
的对应项。
new Regex(@"@(\S+)", RegexOptions.IgnoreCase);
是 '@matched_parameter
' 的匹配项。在通过 <r.Matches(SqlCommandText)[i].ToString();>
获取此项后,接着是修剪工作。出于某种原因,我无法让 C# 找到纯粹的匹配项。因此,一些匹配项带有 '@lname)
' 或 '@fname,
',所以我进行了修剪。现在,内部参数子字符串的纯匹配字符串存储在 argInCmd
(命令中的参数)中。它通过 Parameter.AddWithValue()
方法添加到 sqlcommand
的参数中,然后就可以打开连接、执行非查询等操作了。
Using the Code
此源代码包含一个类库和一个测试窗体。为简洁起见,我使用了两个单独的 listbox
控件来查看来自 SqlClassFactory
DLL 的数据库和相关表列表。类库是 SqlClassFactory.dll,主入口是 TestForm.exe。如果您愿意,可以真正使用类库来获取两个列表并在任何地方使用它们。这并不是 SqlClassLibrary
类所做的唯一工作。它还包括一个通用的 <Generator>
类,其中包含 <public static void BeginGeneration(string rawText)>
。此方法从 Class.txt 文本文件中获取 rawText string
并替换所有 <...\> 为适当的名称。这是 Class.txt 的一个示例部分
namespace <database_name>
{ public class <table_name>
{ <column_name>
SqlDataAdapter sqlDA = null;
SqlConnection sqlConnection = null;
下面是 TestForm
应用程序的核心
SqlClassFactory.Generator.BeginGeneration(rtxtClass.Text);
SqlClassFactory.Generator.AddTableDBName(tableName, dbName);
foreach (DataRow row in dtColumns.Rows)
{
SqlClassFactory.Generator.AddField
(row["DATA_TYPE"].ToString(), row["COLUMN_NAME"].ToString());
}
SqlClassFactory.Generator.EndGeneration();
rtxtClass.Text = SqlClassFactory.Generator.RawClassText;
这里 BeginGenerator() static
方法从 richtextbox rtxtClass
获取 Text
数据,并将其内部化到 SqlClassFactory.Generator
类中。然后 Generator.AddTableDBName(..)
方法替换 rtxtClass.Text string
中的 "<database_name>
" 和 "<table_name>
" 为适当的名称。然后使用 Generator Static
类的 AddField()
方法的 foreach
循环,每次都将属性名称添加到原始类字符串中,该字符串来自 Class.txt。此方法依次替换 "<column_name>
" 为属性名称——即给定数据库中给定表的列名——正如您从上面给出的参数中所见
row["DATA_TYPE"].ToString(), row["COLUMN_NAME"].ToString()
此函数值得关注。在该函数中,存在以下关键代码行
string fieldText =
String.Format("public {0} {1}", propertyType, propertyName)
+ " { get; set; }\n\t\t\t<column_name>";
RawClassText = RawClassText.Replace("<column_name>", fieldText);
这里,每次调用 AddField()
时,fieldtext
首先设置为 "public int productId { get; set; } <column_name>
",第一个是属性类型,第二个是属性名,第三个是 column_name
标志,用于下一次 AddField()
调用,当调用时它会为新属性设置新行并添加第二个新行 "<column_name>
"。
SqlClassFactory.Generator.EndGeneration();
此行代码将 "<column_name>
" string
替换为 String.Empty
,从而删除多余的内容。
rtxtClass.Text = SqlClassFactory.Generator.RawClassText;
通过这行代码,布局字符串被更改为结果字符串。
关注点
我了解到列名表也可以由行组成。:)
历史
这是第一个版本。