MS SQL Server 数据库的包装代码生成器
该应用程序可自动为 MS SQL Server 数据库生成数据库包装器类。我在与同事讨论此事时收到了许多好评和评论。现在应用程序已建成,让我们看看您对此有何看法。
引言
如今,应用程序开发范例已扩展到许多领域。开发人员/架构师通过快速开发应用程序,采用许多不同的方法来跟上快速发展的行业。在所有情况下,最终目标是他们开发的系统的速度、准确性、可伸缩性和性能。考虑到这一点,编写一个相当大的数据库的数据库操作(模型)类可能既繁琐又容易出错,许多开发人员每次都使用复制粘贴的解决方案。这种重复促使人们考虑基于代码生成技术的替代方法,即自动生成数据库包装器的代码。本文旨在响应快速交付数据库驱动系统的需求,如果开发团队很小,则更可取。
在本文中,我既不强迫开发人员采用任何特定的开发方法或流程,也不强迫使用任何特定的架构来开发系统。就我迄今收集到的评论而言,这种代码生成工具最适合那些拥有分离类集来操作数据库操作的应用程序。
该应用程序生成两种类型的类,一种是一组 C#(以下简称 CS)文件,这些文件映射到数据库表。它们的类名与表名相同,并且它们的属性与表属性相同。另一种是一组 CS 文件,涵盖基本的/标准的数据库操作,例如添加、删除、更新(整行或单独每一列)和选择(整行或单独每一列)。如上图所示,开发人员可以直接将自动生成的代码插入其应用程序的架构中。代码生成工具只会生成一组可预测的方法,如所述,目前,该工具不会响应数据库架构中的关系或复杂的、复杂的数据挖掘选项。
背景
在实践中,在完全或部分开发应用程序后更改数据库架构对于今天的许多开发人员来说是一个相当普遍的经验。许多时候,系统规范在系统的开发阶段发生了变化,或者我们采用的开发方法需要这种类型的重构/灵活性。然后,一旦您更改了数据库,作为并行过程,您必须更改模型类(如果您遵循 MVC – 模型-视图-控制器;架构),换句话说,处理数据库操作和操作的类集。这非常耗时,并且会降低开发人员的热情。本文提出了一种自动化方法来彻底解决该问题。在那里,应用程序将开发一组负责执行大部分数据库操作的类,例如添加、删除、更新和选择字段和记录。
使用应用程序
- 应用程序的输入
- 您运行 MS SQL Server 的机器的服务器名称或 IP(Internet 协议)地址。
- 数据库的名称。
- 超级用户凭证,即登录数据库服务器的用户名和密码。
- 添加“命名空间”属性目前不活动。
- 进程
- 单击名为“连接并获取数据”的按钮。
- 单击名为“创建模型类”的按钮。
- 应用程序的输出
- 在应用程序的工作文件夹中生成一组 CS 文件(用于执行所有基本的数据库操作,如添加、编辑、删除、选择等),每个文件映射数据库中的一个表。
应用程序概述
图示了应用程序的示例输出。在这个例子中,我们使用了“Sale”表作为该应用程序的源数据库表。一旦您提供了“使用应用程序”部分中描述的所有必需详细信息,它就会创建两个名为“Sale
”和“HandlerSale
”的 CS 类。“Sale
”类是将列名映射到表的类。由于该表包含两个名为“saleid”和“description”的列,因此“Sale
”类具有名为 saleid
和 description
的两个属性。“HandlerSale
”类拥有执行所有基本数据库操作的代码。Handler 类使用“Sale
”类型的对象作为其方法所需的参数和/或返回类型。应用程序会生成所有 T-SQL 以及参数。这些内容也将驻留在“Handler”类中。简而言之,一旦应用程序生成了类,您就无需做任何事情,只需直接将它们添加到您的项目中。
要求
阅读/测试本文的用户需要对 C# Windows 窗体应用程序、MS SQL Server 和 T-SQL 有一定的了解。此外,您应该在您的机器上安装 Visual Studio .NET IDE 和 MS SQL Server 7 或更高版本(需要超级用户凭证才能登录 DB 服务器)。毕竟,如果您熟悉流行的应用程序开发架构,如 MVC,那将是额外的优势。
注意:如果您计划使用驻留在网络机器上的 SQL Server,您需要在您的机器上安装“SQL Server 企业管理器”来创建测试数据库,或者您需要与您友好的网络管理员联系为您创建测试数据库。
重要:要使用该代码,您必须将对“Microsoft.ApplicationBlocks.Data.dll”的引用添加到“CSharp_Wrapper_src”目录的 DLL 文件夹中。
使用代码
该应用程序包含四个类,分别命名为
ModelCreatorFrm
- 应用程序的主用户界面。DynamicSqlGenerator
- 负责根据从数据库收集的信息动态创建代码的类。Table
- 临时存储有关数据库表详细信息并为每个表生成方法和变量声明的类。Attribute
– 它保存与表中的每个列相关的所有详细信息。此类是Table
类的私有类。
应用程序流程
当用户单击“连接并获取数据”按钮时,应用程序将启动。`Click` 事件在初始化连接字符串并获取 `DataReader` 来读取给定数据库的表数据后,将触发名为 `GetAllTheDetials` 的方法。
private void lbtnConnect_Click(object sender, System.EventArgs e)
{
if (CreateConnectionString())
{
GetAllTheDetials(tcGetDataReader());
this.lbtnCreate.Enabled = true;
}
}
然后,应用程序为每个表创建“Table
”类型的对象,并使用名为“lobjTables
”的 `System.Collection.ArrayList` 将它们存储在内存中。完成此操作后,用户可以单击名为“创建模型类”的第二个按钮,该按钮将触发下面的方法。此方法负责使用“lobjTables
” `ArrayList` 生成代码。
private void lbtnCreate_Click(object sender, System.EventArgs e)
{
StringBuilder sbMapClass, sbHandlerClass;
StreamWriter swMapClass = null;
StreamWriter swHandlerClass = null;
foreach(Table tb in lobjTables)
{
try
{
//Get the class name for the Database Map class
//Example Asset.cs
sbMapClass = new System.Text.StringBuilder(tb.Name);
sbMapClass.Append(".cs");
//Get the Class name for the DB handler class
//Example HandlerAsset.cs
sbHandlerClass = new StringBuilder("Handler");
sbHandlerClass.Append(tb.Name);
sbHandlerClass.Append(".cs");
FileInfo lobjFileInfoMapClass = new FileInfo(
sbMapClass.ToString());
FileInfo lobjFileInfoHandlerClass = new FileInfo(
sbHandlerClass.ToString());
swMapClass = lobjFileInfoMapClass.CreateText();
swHandlerClass = lobjFileInfoHandlerClass.CreateText();
swMapClass.Write(tb.MapClassTopPartCode.ToString());
swMapClass.Write(tb.MapClassMidPartCode.ToString());
swMapClass.Write(tb.MapClassBottomPart.ToString());
swHandlerClass.Write(tb.HandlerClassCode.ToString());
}
//The file name is a directory.
catch (System.UnauthorizedAccessException ev)
{
MessageBox.Show(ev.Message);
}
//The disk is read-only.
catch (System.IO.IOException ev)
{
MessageBox.Show(ev.Message);
}
//The caller does not have the required permission.
catch (System.Security.SecurityException ev)
{
MessageBox.Show(ev.Message);
}
finally
{
swMapClass.Flush();
swHandlerClass.Flush();
swMapClass.Close();
swHandlerClass.Close();
}
}
MessageBox.Show("Done !!");
this.lbtnCreate.Enabled = false;
}
从数据库获取表的名称、属性和类型
在上面的代码中,最有趣和最重要的部分之一是用于获取数据库的表名称列表以及其他附加数据的 T-SQL 命令。
private const string SQL_GET_TABLES = "SELECT
table_name,
column_name,
data_type FROM information_schema.columns
WHERE table_name in (select table_name
FROM Information_Schema.Tables
WHERE Table_Type='Base Table')
ORDER BY table_name";
请注意,您可以使用“*”来查看除 `table_name`、`column_name` 和 `data_type` 之外的所有选择。
获取表的が主键
另一个重要的 T-SQL 命令是用于选择给定表的が主键的命令。
private const string SQL_SELECT_PRIMARYKEY = "SELECT
column_name
FROM information_schema.key_column_usage
WHERE constraint_name like 'pk%'
and table_name=@tablename";
完成后,您应该得到一组已注释和格式化的 CS 类,其中每个数据库表都有两个类。下面您可以看到为名为 _Sale_ 的表生成的类。
为 _Sale_ 表生成的 _Sale_ 类
using System;
public class Sale
{
/// <SUMMARY>
/// Default Contructor
/// <SUMMARY>
public Sale()
{}
public string saleid
{
get { return _saleid; }
set { _saleid = value; }
}
private string _saleid;
public string description
{
get { return _description; }
set { _description = value; }
}
private string _description;
public Sale(
string saleid,
string description)
{
this._saleid = saleid;
this._description = description;
}
}
为 _Sale_ 表生成的 _HandlerSale_ 类
using System;
using System.Data.SqlClient;
using System.Data;
using System.IO;
using System.Text;
using Microsoft.ApplicationBlocks.Data;
/// <summary>
/// Summary description for HandlerTBN.
/// </summary>
public class HandlerSale
{
private const string SQL_CONN_STRING
= "data source=PRD-01;initial catalog=ManGoDB;"
+ "integrated security=false;persist" +
" security info=True;User ID=sa;Password=d7972";
private const string SQL_DELETE_Sale
= "DELETE FROM Sale WHERE saleid = @saleid";
private const string SQL_SELECT_Sale
= "SELECT * FROM Sale WHERE saleid = @saleid";
private const string SQL_INSERT_Sale
= "INSERT INTO Sale VALUES(@saleid, @description)";
private const string SQL_UPDATE_Sale
= "UPDATE Sale SET saleid = @saleid," +
" description = @description WHERE saleid = @saleid";
private const string SQL_UPDATE_description
= "UPDATE Sale SET description = @description WHERE saleid = @saleid";
private const string SQL_SELECT_description
= "SELECT description FROM Sale WHERE saleid = @saleid";
private const string PARAM_saleid
= "@saleid";
private const string PARAM_description
= "@description";
public HandlerSale()
{
//
// TODO: Add constructor logic here
//
}
/// <summary>
/// Insert a New TBN record to the table
/// </summary>
/// <param name="tobjTBN">Object to be inserted to the table</param>
/// <returns>Status of the data insertion</returns>
public bool InsertSale(Sale tobjSale)
{
if(tobjSale != null)
{
//Get the parameter list needed by the given object
SqlParameter[] lParamArray = GetParameters(tobjSale);
SetParameters(lParamArray, tobjSale);
//Get the connection
SqlConnection con = GetConnection(SQL_CONN_STRING);
if (con == null)
//Connection is not created
return false;
//Execute the insertion
int i = SqlHelper.ExecuteNonQuery(
con,
CommandType.Text,
SQL_INSERT_Sale,
lParamArray);
//Dispose the Sql connection
con.Dispose();
if (i ==1)
//Done and insert the object to the table
return true;
else
//Fail to execute teh insertion
return false;
}
else
//No object found to insert
return false;
}
/// <summary>
/// Get a Object given the Object ID
/// </summary>
/// <param name="tstrObjectId"></param>
/// <returns></returns>
public Sale tcSelectSale(string tstrsaleid)
{
// SqlConnection that will be used to execute the sql commands
SqlConnection connection = null;
SqlParameter[] aParms =
new SqlParameter[]{
new SqlParameter(PARAM_saleid
, tstrsaleid)};
try
{
try
{
connection = GetConnection(SQL_CONN_STRING);
}
catch (System.Exception e)
{
//lobjError = ManGoErrors.ERROR_CONNECT_TO_DB;
//log.Error(lobjError, e);
return null;
}
// Call ExecuteDataReader static method of
// SqlHelper class that returns an DataReader
// We pass in an open database connection object
// , command type, and command text
SqlDataReader reader = SqlHelper.ExecuteReader(
connection
, CommandType.Text
, SQL_SELECT_Sale
, aParms);
// read the contents of data reader and return the results:
while (reader.Read())
{
return new Sale(
reader.GetString(0),
reader.GetString(1));
}
// close Reader
reader.Close();
return null;
}
catch(Exception ex)
{
//lobjError = ManGoErrors.ERROR_UNKNOWN;
System.Diagnostics.Trace.WriteLine(ex.Message);
return null;
}
finally
{
if(connection != null)
connection.Dispose();
}
}
/// <summary>
/// Get a Column given the Object ID
/// </summary>
/// <param name="tstrObjectId"></param>
/// <returns></returns>
public string tcSelectdescription(string tstrsaleid)
{
// SqlConnection that will be used to execute the sql commands
SqlConnection connection = null;
SqlParameter[] aParms = new SqlParameter[]{
new SqlParameter(
PARAM_saleid
, tstrsaleid)};
try
{
try
{
connection = GetConnection(SQL_CONN_STRING);
}
catch (System.Exception e)
{
//lobjError = ManGoErrors.ERROR_CONNECT_TO_DB;
//log.Error(lobjError, e);
return string.Empty;
}
// Call ExecuteReader static method of
// SqlHelper class that returns an DataReader
// We pass in an open database connection object
// , command type, and command text
SqlDataReader reader = SqlHelper.ExecuteReader(
connection
, CommandType.Text
, SQL_SELECT_description
, aParms);
// read the contents of Data reader and return the result:
while (reader.Read())
{
return reader.GetString(0);
}
// close Reader
reader.Close();
return string.Empty;
}
catch(Exception ex)
{
//lobjError = ManGoErrors.ERROR_UNKNOWN;
System.Diagnostics.Trace.WriteLine(ex.Message);
return string.Empty;
}
finally
{
if(connection != null)
connection.Dispose();
}
}
/// <summary>
/// Delete the Profile from the Profile table
/// </summary>
/// <param name="tstrObjectId"></param>
/// <returns></returns>
public bool tcDeleteSale(string tstrsaleid)
{
SqlConnection connection = null;
SqlParameter[] aParms = new SqlParameter[]{
new SqlParameter(
PARAM_saleid
, tstrsaleid)};
try
{
connection = GetConnection(SQL_CONN_STRING);
if (connection == null)
return false;
// Call ExecuteNoneQuery static method
// of SqlHelper class that returns an Int
// We pass in an open database connection object
// , command type, and command text
int i = SqlHelper.ExecuteNonQuery(
connection
, CommandType.Text
, SQL_DELETE_Sale
, aParms);
return true;
}
catch(Exception ex)
{
//lobjError = ManGoErrors.ERROR_UNKNOWN;
System.Diagnostics.Trace.WriteLine(ex.Message);
return false;
}
finally
{
if(connection != null)
connection.Dispose();
}
}
/// <summary>
/// Update a object given the object primary key
/// </summary>
/// <param name="tstrobjectId"></param>
/// <returns></returns>
public void tcUpdateSale(Sale tobjSale)
{
SqlParameter[] aParms = GetParameters(tobjSale);
SetParameters(aParms, tobjSale);
using (SqlConnection conn = GetConnection(SQL_CONN_STRING))
{
// conn.Open();
using (SqlTransaction trans = conn.BeginTransaction())
{
try
{
SqlHelper.ExecuteNonQuery(
trans
, CommandType.Text
, SQL_UPDATE_Sale, aParms);
trans.Commit();
}
catch(System.Exception e)
{
trans.Rollback();
//log.Error(lobjError, e);
throw;
}
}
}
}
/// <summary>
/// Update an Item given the object primary key
/// </summary>
/// <param name="tstrobjectId"></param>
/// <returns></returns>
public void tcUpdatedescription(
string tstrItemId
, string tobjData)
{
SqlParameter[] aParms = new SqlParameter[]{
new SqlParameter(
PARAM_saleid
, tstrItemId)
, new SqlParameter(PARAM_description, tobjData)};
using (SqlConnection conn = GetConnection(SQL_CONN_STRING))
{
// conn.Open();
using (SqlTransaction trans = conn.BeginTransaction())
{
try
{
SqlHelper.ExecuteNonQuery(
trans
, CommandType.Text
, SQL_UPDATE_description
, aParms);
trans.Commit();
}
catch(System.Exception e)
{
trans.Rollback();
System.Diagnostics.Trace.WriteLine(e.Message);
throw;
}
}
}
}
/// <summary>
/// Get the Parameter List for this object
/// </summary>
/// <param name="tobjTBN">TBN object</param>
/// <returns>Sql Parameter List</returns>
private SqlParameter[] GetParameters(Sale tobjSale)
{
SqlParameter[] objParamArray = SqlHelperParameterCache.GetCachedParameterSet(
SQL_CONN_STRING
, SQL_INSERT_Sale);
if (objParamArray == null)
{
//Represents a parameter to a System.Data.SqlClient.SqlCommand,
//and optionally, its mapping to System.Data.DataSet columns.
objParamArray = new SqlParameter[]
{
new SqlParameter(PARAM_saleid, tobjSale.saleid),
new SqlParameter(PARAM_description, tobjSale.description),
};
SqlHelperParameterCache.CacheParameterSet(
SQL_CONN_STRING
, SQL_INSERT_Sale
, objParamArray);
}
return objParamArray;
}
/// <summary>
/// Fill database parameters for a perticular profile
/// </summary>
private void SetParameters(SqlParameter[] SaleParms,Sale tobjSale)
{
SaleParms[0].Value = tobjSale.saleid;
SaleParms[0].Value = tobjSale.description;
}
/// <summary>
/// Create data base conection
/// </summary>
/// <param name="connectionString"></param>
/// <returns></returns>
private SqlConnection GetConnection(string tstrConnectionString)
{
//Represents an open connection to a SQL Server database.
//This class cannot be inherited.
SqlConnection dbconnection = new SqlConnection(tstrConnectionString);
try
{
//Opens a database connection with the property settings
//specified by the ConnectionString.
dbconnection.Open();
return dbconnection;
}
//Cannot open a connection without specifying a data source.
//or The connection is already open.
catch (System.InvalidOperationException e)
{
return null;
}
//A connection-level error occurred while opening.
catch (System.Data.SqlClient.SqlException e)
{
return null;
}
}
}
关注点
代码的某些部分仍需要改进。同时,在我看来,该应用程序可以得到极大的改进,使其能够处理多个数据库。