65.9K
CodeProject 正在变化。 阅读更多。
Home

适用于 ASP.NET 和 SQL Server 的通用数据库管理员(重载版)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (78投票s)

2005年11月26日

8分钟阅读

viewsIcon

482432

downloadIcon

9621

一个动态的 Web 应用程序需要一个管理员部分来对数据库中的记录/表执行 CRUD 操作。如果有一个可以插入到任何 Web 应用程序中的数据库管理员,那不是很好吗?只需提供您的 SQL 连接字符串,它就可以在短短五个页面中动态管理所有表操作。

Universal Data Admin in action

新窗口在线演示 - UDA

简介(七页就够了)

本文描述了一个 ASP.NET Web 模块,它可以作为数据库管理模块添加到任何 SQL 驱动的 Web 应用程序中或进行自定义。当我开始为 Web 应用程序创建管理员部分时,我想为什么不尝试制作一些更通用的东西,一个即插即用的管理员部分,可以用于任何 SQL 驱动的动态 Web 应用程序,这就是这项工作的成果。

目标是

  1. 创建一个自给自足的管理员部分,只需更改 web.config 文件中的 SQL Server 的连接字符串,即可将其放入任何 SQL 驱动的 Web 应用程序中。
  2. 用户在执行 CRUD(创建/读取/更新/删除)操作之前需要登录(AdminLite 版本不需要)。
  3. 应该为每个操作(CRUD)提供一个单独的页面,该页面将表名作为参数。
  4. 一个 dataAccess 类,它封装了所有数据访问方法,并按要求返回值或记录。
  5. 表的 CRUD 操作(您可以创建/读取/更新/删除表)。
  6. 使用自定义 SQL 查询过滤记录。
  7. 在数据库上执行自定义 SQL 语句。
  8. 支持分页/排序
  9. 包含使用正则表达式的登录页面的 SQL 注入解决方法 - 最新更新。
  10. 支持多个图像(SQL 表中的二进制数据)。

对于非 IT 人员来说,查看数据库而无需了解任何 SQL 的好方法CK Max

(上述评论是关于 DB Admin 工具的第一阶段。更新版本要先进得多,并且仅面向 IT 用户。)

假设

这些是最初的想法,但为了使其成为一个通用模块,我做了一些假设

  • 每个表都有主键(整数和标识),并且它是表中的第一个字段
  • (AdminLite 版本不需要)SQL 数据库中至少有一个 AdminUser 表,其中有一个权限字段,其值为“Admin”,如下所示,供用户登录管理员部分执行 CRUD 操作。

管理员表结构

Admin table

一个具有 Web 管理模块中所显示值的管理员表

Display Admin

web.config 中有两个键,一个用于连接字符串,另一个用于管理员表的名称

<appSettings>
  <add key="ConnectionString" 
    value="Data Source=YourdatasrcIP;POOLING=FALSE;
           database=YourDatabasename;
           User ID=YouruserID;
           Password=YourPassword" />
  <add key="AdminTable" 
    value="YourAdminTableName_NOT_REQUIRED_IN_ADMINLITE" /> 
</appSettings>

Actions

这些是构成整个模块的动作

  • 数据访问
  • 登录(AdminLite 版本不需要
  • 显示数据库中的所有表
  • 查看表中的记录列表
  • 查看一条记录
  • 编辑一条记录
  • 删除一条记录
  • ------最新更新--------------
  • 创建表
  • 读取表
  • 更新表
  • 删除表
  • 删除表中的所有记录
  • 使用自定义 SQL 查询过滤表中的记录
  • 执行自定义 SQL 语句

数据访问类

有一个标准的DataAccess类,clsDataAccess.cs,它处理所有相关操作。

代码:这里我只保留了函数名,只是为了让您一窥数据访问方法。

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient; 

namespace Admin
{
    public class clsDataAccess
    // Class defination
    {
        public clsDataAccess()
        { }
        SqlConnection mycon = new SqlConnection(
          ConfigurationSettings.AppSettings["ConnectionString"]);
        // Opens database connection with in SQL SERVER
        public bool openConnection()
        // Closes database connection with in SQL SERVER
        public void closeConnection()
        // Getdata from the table required(given in query).
        public SqlDataReader getData(string query)
        // Save data usually,inserts and updates the data in table.
        public void saveData(string query)
        // Save data usually,inserts and updates the data.
        public void saveNewData(string query)
        // Delete data in database depending on the tablename.
        public int DeleteData(string query)
        // Get data by paging using datagrid.
        public SqlDataAdapter getDataforUpdate(string query)
        // Get data by paging using datagrid.
        public DataSet getDatabyPaging(string query)
        // check a particular value to see the validity.
        public int getCheck(string query)
        // Get a value of limit from the database table.
        public string getValue(string query,int j)
        //Log in method 
        public SqlDataReader Login(string query)
        // dynamically get all table names
        public DataTable getTablenames()
        // For Table operations
        public int TableWrite(string query)
    }
}

登录

登录操作(AdminLite 版本不需要

Login screen

对用户登录过程的基本身份验证会检查用户是否具有管理员权限。

代码

string uname;
uname = txtemail.Text.ToString();

string pass;
pass = txtpwd.Text.ToString();

string s;

s = "SELECT * FROM " + 
    ConfigurationSettings.AppSettings["AdminTable"] +
    " where Username ='" + uname + 
    "' AND Password ='" + pass + "'"  ;
//Login class is called
clsDataAccess mylogin = new clsDataAccess();
SqlDataReader mydr1 = mylogin.Login(s);
    
while(mydr1.Read())
{
    if (mydr1.GetValue(6).ToString().Trim()  == "Admin")
    {
        Session["userfullname"] = 
           mydr1.GetValue(3).ToString().Trim() +
           " " + mydr1.GetValue(4).ToString().Trim();
        Session["Admin"] = "true";
        Response.Redirect("DBMain.aspx");
    }
    else
    {
        lblError.Text=  "Error ! - Invalid Login";
    }
}
lblError.Text= "Error ! - Invalid Login" ;
mydr1.Close();

其次,在所有页面中,都会检查用户是否已登录,否则他们将被重定向到登录页面。

SQL 注入解决方法

Login screen

代码

private bool CheckSQLInjection(string s)
{
    bool flagSQLI = false;
    try
    {
        if ( !Regex.IsMatch(s, @"^[a-zA-Z._s]{1,50}$"))
        {
            flagSQLI = false;
        }
        else
        {
            flagSQLI = true;
        }
    }
    catch (Exception ex)
    {
        flagSQLI = true;
    }

    if (flagSQLI)
        return true;
    else
        return false;
}

代码用于检查会话中是否存在管理员权限。

代码

<%
{
    if ((string)Session["Admin"] == "true")
    {
    }
    else
        Response.Redirect("index.aspx");
}%>

显示数据库中的所有表

用户登录后,需要查看数据库中有哪些表。此页面也可以通用化(通过查询数据库以获取所有表并排除系统表)。

但通常,数据库中的表名类似于 APT_TABLE1XP_TABLE2。在这种情况下,这些名称对可能不是处理表的技术人员的管理员用户来说不是很有帮助,所以让我们创建一个简单的 HTML 页面,其中包含用户友好的表名,并链接到所有 CRUD 操作(AdminLite 版本不需要)。

2005 年 11 月 29 日新增,仅限 AdminLite 版本

在 AdminLite 版本中,使用架构来获取所有表名。

为 AdminLite 添加代码
public DataTable getTablenames()
{
    SqlDataAdapter da = 
      new SqlDataAdapter("SELECT * FROM Information_Schema.Tables
      where Table_Type = 'BASE TABLE'", mycon);
    DataTable dt = new DataTable();
    da.Fill(dt);
    return dt;
}
foreach (DataRow row in myT.Rows)
{
    Response.Write (row["TABLE_NAME"] + "<hr>");
}

选择表操作

Tables Page

输出包含指向显示详细信息页面的链接。

代码(AdminLite 版本无需自定义)。

<A href="db.aspx?table=EventsTableName">Events and News</A>

查看表中的记录列表

选择表后,以下功能对于管理员查看/浏览记录并对其进行操作非常重要

  • 我们需要显示表中的记录列表。
  • 每条记录的 CRUD 操作链接。
  • 列表分页。
  • 按列名排序(默认主键 ID)。

DataGrid 是最佳选择,因为它内置了分页和排序功能。结果应该看起来像这样

记录列表操作

Table Details

代码

您需要找出第一个键,即主键,它可以存储在会话变量中

Session["FirstKey"] = GetFirstKeys();
private string GetFirstKeys()
{
    string returnvalue = "Node";
    DataTable dt = new DataTable();
    Session["tName"] = 
       Request.QueryString["table"].ToString().Trim();
    string query;
    query = "select * from " + 
       Request.QueryString["table"].ToString().Trim() ;
    clsDataAccess myclass = new clsDataAccess();
    myclass.openConnection();
    mydr = myclass.getDatabyPaging(query);
    returnvalue = mydr.Tables[0].Columns[0].Caption;
    mydr.Clear();
    return returnvalue;
}

在页面加载时,将 DataGrid 绑定到数据源

if (!IsPostBack)
{
    BindDataGrid();
}

我添加了一个集合来创建一个数据源,参数为 tablename,该参数来自查询字符串,并将表名添加到会话中以供以后显示使用。此表将在大多数页面中使用。

ICollection CreateDataSource()   
{
    DataTable dt = new DataTable(); 
    Session["tName"] = 
         Request.QueryString["table"].ToString().Trim();
    string query;
    //query = "select * from " + 
    //       Request.QueryString["table"].ToString().Trim() ; 

    // added for custom sql query
    query = Session["qry"].ToString() ;
    clsDataAccess myclass = new clsDataAccess();
    myclass.openConnection();
    mydr = myclass.getDatabyPaging(query);
    TempTable = new DataTable();
    TempTable = mydr.Tables[0];
    TempTableView = new DataView(TempTable);
    TempTableView.Sort = SortField;
    return TempTableView;
}

DGTableDataGrid 的名称。其他函数用于使用数据源绑定 DataGrid 以及分页/排序等。

private void DGtable_PageIndexChanged(object source,
        System.Web.UI.WebControls.DataGridPageChangedEventArgs e)
{
    DGtable.CurrentPageIndex=e.NewPageIndex;
    BindDataGrid();
}

DataGrid 添加三个用于查看/编辑和删除链接的列。这必须动态完成,因为我们事先不知道主键的名称。所以,让我们在页面加载事件中添加它。

你们中的一些人可能在想“为什么是第一个键?”。嗯,快速回答是,当您向 DataGrid 传递参数时

Viewdata.aspx?id={0}

您必须在设计时提供它。您可以尝试找到主键,获取列数,然后动态添加该计数作为 DataGrid 的参数。当然,您不会因此获得诺贝尔奖。所以这是一个简单快捷的解决方案。

private void BindDataGrid
{
    Session["FirstKey"] = GetFirstKeys();
    HyperLinkColumn urlView = new HyperLinkColumn();
    urlView.Text = "View";
    urlView.DataNavigateUrlField = Session["FirstKey"].ToString() ;
    urlView.HeaderText = "View";
    urlView.DataNavigateUrlFormatString = "Viewdata.aspx?id={0}";

    HyperLinkColumn urlEdit = new HyperLinkColumn();
    urlEdit.Text = "Edit";
    urlEdit.DataNavigateUrlField = Session["FirstKey"].ToString() ;
    urlEdit.HeaderText = "Edit";
    urlEdit.DataNavigateUrlFormatString = "Editdata.aspx?id={0}";

    HyperLinkColumn urlDelete= new HyperLinkColumn();
    urlDelete.Text = "Delete";
    urlDelete.DataNavigateUrlField = Session["FirstKey"].ToString() ;
    urlDelete.HeaderText = "Delete";
    urlDelete.DataNavigateUrlFormatString = "Deletedata.aspx?id={0}";
    DGtable.Columns.Add(urlView);
    DGtable.Columns.Add(urlEdit);
    DGtable.Columns.Add(urlDelete);
    DGtable.DataSource = CreateDataSource();
    DGtable.DataBind();   
}

对于排序,同样,一旦您单击字段,它将按升序排序。我们可以进一步开发此功能以支持升序和降序。

private void DGtable_SortCommand(object source,
        System.Web.UI.WebControls.DataGridSortCommandEventArgs e)
{
    SortField = (string)e.SortExpression;
    lblstatus.Text = "VIEW RECORDS OF TABLE: [" + 
                     Request.QueryString["table"].ToString() + 
                     "] Sorted by '<i>"
                     + Session["FirstKey"]+ "</i>'";
    BindDataGrid();
}

查看一条记录

查看操作

View Records

代码

我使用了 Literal 控件来动态创建控件,添加到 TableCell 对象,并添加方法将其添加到表中。

TableCell c = new TableCell();
c.Controls.Add(new LiteralControl("<b>Value</b>"));
myReader.GetName(j).ToString();

给出表的字段名。

myReader.GetValue(j).ToString();

给出表的值。

private void ShowData()
{
    string s="";

    SqlDataReader myReader;
    int i =0, j=0,k=0;

    i = Convert.ToInt32(Request.QueryString["id"]);
    s = "SELECT * FROM " + Session["tName"].ToString() + 
        " where " + Session["FirstKey"] + " =" + i ;

    clsDataAccess myclass = new clsDataAccess();
    myclass.openConnection();
    myReader = myclass.getData(s);
   
    while (myReader.Read())
    {
        for(j=-1; j<myReader.FieldCount;j++)
        {
            TableRow r = new TableRow();
            for (k=0; k<3; k++)
            {
                TableCell c = new TableCell();

                if (j==-1)
                {
                    if (k == 0)
                    {
                        c.Width = 15;
                        c.Controls.Add(new LiteralControl("<b>Sno.lt;/b>"));
                    }
                    if (k == 1)
                    {
                        c.Width = 50;
                        c.Controls.Add(new LiteralControl("<b>Column</b>"));
                    }
                    if (k == 2)
                    {
                        c.Width = 50;
                        c.Controls.Add(new LiteralControl("<b>Value</b>"));
                    }
                }
                if(j>-1)
                {
                    if (k == 0)
                    {
                        c.Width = 15;
                        c.Controls.Add(new LiteralControl((j+1).ToString()));
                    }
                    if (k == 1)
                    {
                        c.Width = 50;
                        if (myReader.GetName(j).ToString()=="ID")
                            c.Controls.Add(new LiteralControl("<i>&nbsp;"  +
                                 myReader.GetName(j).ToString() + "</i>"));
                        else
                            c.Controls.Add(new LiteralControl("&nbsp;" +
                                 myReader.GetName(j).ToString() + "&nbsp;" ));
                    }
                    if (k == 2)
                    {
                        c.Width = 300;
                        c.Controls.Add(new LiteralControl("&nbsp;" +
                            myReader.GetValue(j).ToString() + "&nbsp;" ));
                    }
                }
                c.VerticalAlign = VerticalAlign.Top;
                c.Height = Unit.Pixel(22);
                r.Cells.Add(c);
            }
            Table2.Rows.Add(r);
        }
    }
    myReader.Close();
    myclass.closeConnection();
}

编辑一条记录

编辑记录是棘手的部分。尽管我已尽力提供通用的编辑功能,但此部分可以进一步开发以包含下拉列表、单选按钮、复选框等。

如果字段是主键,则锁定它

if (myReader.GetName(j).ToString()==Session["FirstKey"])
{
    TextBox1.ReadOnly = true;
    TextBox1.BackColor = Color.GhostWhite ;
}

获取数据类型

myReader.GetFieldType(j).ToString()

如果数据是值,则创建一个 TextBox

c.Controls.Add(TextBox1);

编辑操作

Edit Records

代码

private void ShowData()
{
    string s="";

    SqlDataReader myReader;
    int i =0, j=0,k=0;

    i = Convert.ToInt32(Request.QueryString["id"]);
    s = "SELECT * FROM " + Session["tName"].ToString() + 
        " where " + Session["FirstKey"] + "=" + i ;
    clsDataAccess myclass = new clsDataAccess();
    myclass.openConnection();
    myReader = myclass.getData(s);
    while (myReader.Read())
    {
        for(j=-1; j<myReader.FieldCount;j++)
        {
            TableRow r = new TableRow();
            for (k=0; k<4; k++)
            {
                TableCell c = new TableCell();
                if (j==-1)
                {
                    if (k == 0)
                    {
                        c.Width = 15;
                        c.Controls.Add(new LiteralControl("<b>Sno.</b>"));
                    }
                    if (k == 1)
                    {
                        c.Width = 50;
                        c.Controls.Add(new LiteralControl("<b>Column</b>"));
                    }
                    if (k == 2)
                    {
                        c.Width = 50;
                        c.Controls.Add(new LiteralControl("<b>Value</b>"));
                    }
                    if (k == 3)
                    {
                        c.Width = 35;
                        c.Controls.Add(new LiteralControl("<b>&nbsp;Datatype</b>"));
                    }
                }
                if(j>-1)
                {
                    TextBox TextBox1 = new TextBox();
                    TextBox1.ID = "Text" + j;
                    TextBox1.Text =  myReader.GetValue(j).ToString();
                    if (myReader.GetName(j).ToString()==Session["FirstKey"])
                    {
                        TextBox1.ReadOnly = true;
                        TextBox1.BackColor = Color.GhostWhite;
                    }
                    TextBox1.Width = 200;
                    if (k == 0)
                    {
                        c.Width = 15;
                        c.Controls.Add(new LiteralControl((j+1).ToString()));
                    }
                    if (k == 1)
                    {
                        c.Width = 50;
                        if (myReader.GetName(j).ToString()==Session["FirstKey"])
                            c.Controls.Add(new LiteralControl("<i>&nbsp;"  + 
                                myReader.GetName(j).ToString() + "</i>"));
                        else
                            c.Controls.Add(new LiteralControl("&mp;nbsp;" + 
                                myReader.GetName(j).ToString() + "&nbsp;" ));
                    }
                    if (k == 2)
                    {
                        c.Width = 300;
                        c.Controls.Add(TextBox1);
                    }
                    if (k == 3)
                    {
                        c.Width = 35;
                        c.Controls.Add(new LiteralControl("&nbsp;" + 
                              myReader.GetFieldType(j).ToString() + "&nbsp;" ));
                    }
                }
                r.Cells.Add(c);
            }
            Table2.Rows.Add(r);
        }
    }
    myReader.Close();
    myclass.closeConnection();
}

更改已提交

private void btnSubmit_Click(object sender, System.EventArgs e)
{
    string tname;
    tname = Session["tName"].ToString();
    try
    {
        string s;
        SqlDataAdapter myDA = new SqlDataAdapter();
        DataSet myDataSet = new DataSet();
        int i =0;

        i = Convert.ToInt32(Request.QueryString["id"]);
        s = "SELECT * FROM " + Session["tName"].ToString() + 
            " where " + Session["FirstKey"] + "=" + i ;
        clsDataAccess myclass = new clsDataAccess();
        myclass.openConnection();
        myDA = myclass.getDataforUpdate(s);
        SqlCommandBuilder mySCB = new SqlCommandBuilder(myDA);
        myDA.Fill(myDataSet,tname);
        DataTable myTable;
        myTable =myDataSet.Tables[0];

        for (i =1;i < Request.Form.Count-2;i++)
        {
            myTable.Rows[0][i] = Request.Form[i+1].ToString();
        }
        myDA.Update(myDataSet,tname);
        myDataSet.AcceptChanges();
        lblstatus.Text = " Updated Successfully !!!";
    }
    catch(Exception f)
    {
        lblstatus.Text = f.Message + " Error while Updating";
        lblstatus.Font.Bold = true;
        lblstatus.ForeColor = Color.Red;
    }
    finally
    {
    }
}

删除一条记录

删除操作

Delet a Record

代码

string tname;
tname = Session["tName"].ToString();
try
{
    string s;
    SqlDataAdapter myDA = new SqlDataAdapter();
    DataSet myDataSet = new DataSet();
    int i =0;

    i = Convert.ToInt32(Request.QueryString["id"]);
    s = "SELECT * FROM " + Session["tName"].ToString() + 
        " where " + Session["FirstKey"] + "=" + i ;
    clsDataAccess myclass = new clsDataAccess();
    myclass.openConnection();
    myDA = myclass.getDataforUpdate(s);

    SqlCommandBuilder mySCB = new SqlCommandBuilder(myDA);
    myDA.Fill(myDataSet,tname);
    DataTable myTable;

    myTable =myDataSet.Tables[0];
    DataRowCollection rc = myTable.Rows;
    rc[0].Delete();

    myDA.Update(myDataSet,tname);
    myDataSet.AcceptChanges();
    lblstatus.Text = " Deleted Successfully !!!";
}
catch(Exception f)
{
    lblstatus.Text = f.Message + " Error while Updating";
    lblstatus.Font.Bold = true; 
    lblstatus.ForeColor = Color.Red; 
}
finally
{
}

创建表

创建表操作

Create table in action

代码(尽可能简单)

string s = "Create Table " + sTable + 
           " ([ID] INTEGER PRIMARY KEY IDENTITY)";
clsDataAccess myclass = new clsDataAccess();
myclass.openConnection();
int i = myclass.TableWrite(s);
public int TableWrite(string query)
{
    SqlCommand sqlCommand = new SqlCommand();
    sqlCommand.CommandText= query;
    sqlCommand.Connection=mycon;
    return sqlCommand.ExecuteNonQuery();
}

读取表

读取表操作

Read table in action

代码

SqlDataReader myReader;

s = "SELECT * FROM " + sTable;    
clsDataAccess myclass = new clsDataAccess();

myclass.openConnection();    
myReader = myclass.getData(s);

DataTable myT; 
myT = myReader.GetSchemaTable();
    
foreach (DataRow myField in myT.Rows)
{
    //For each property of the field...
    foreach (DataColumn myProperty in myT.Columns)
    {
        //Display the field name and value.
        response.Write(myProperty.ColumnName + 
           " = " + myField[myProperty].ToString());
    }
}
  • myField["ColumnName"].ToString()
  • myField["DataType"].ToString()
  • myField["ColumnSize"].ToString()
  • myField["AllowDBNull"].ToString()

给出字段的相应值。

删除表

删除表操作

Sample screenshot

代码

string s = "Drop Table " + sTable ; 
clsDataAccess myclass = new clsDataAccess();

myclass.openConnection();    
int i = myclass.TableWrite(s);

删除表中的所有记录

string s = "Truncate Table " + sTable ;

clsDataAccess myclass = new clsDataAccess();
myclass.openConnection();    
int i = myclass.TableWrite(s);

使用自定义 SQL 查询过滤表中的记录

过滤记录操作

Grid View

代码

Session["qry"] = txtSQL.Text ; // simple :)

执行自定义 SQL 语句

执行自定义 SQL 语句操作

Sample screenshot

代码

if (input.Substring(0,1) == "'")
else    
{
    clsDataAccess myclass = new clsDataAccess();
    myclass.openConnection();

    int j = myclass.TableWrite(input);
}

添加/编辑字段

在操作中添加/编辑字段

Sample screenshot for edit field

代码

sDatatype = (Request.Form["datatype"]).Trim();
sField = (Request.Form["field"]).Trim();
sLength = (Request.Form["length"]).Trim();
sDefault = (Request.Form["default"]).Trim();
sAllowNull = (Request.Form["allownull"]).Trim();

if ((String.Compare(sEdit,"true"))==0)
{
    s =  "Alter Table " + sTable + "  Alter Column  " + 
         sField + " " + sDatatype ;

    if (sLength != "")
    {
        s =  s + " (" + sLength + ")";
    }
    if (sDefault != "")
    {
        s = s + " DEFAULT '" + sDefault + "' ";
    }
    if (sAllowNull != "")
    {
        s = s + " " + sAllowNull + "";
    }
}
else
{
    s =  "Alter Table " + sTable + " ADD " + 
         sField + " " + sDatatype ;
    if (sLength != "")
    {
        s =  s + " (" + sLength + ")";
    }
    if (sDefault != "")
    {
        s = s + " DEFAULT '" + sDefault + "' ";
    }
    if (sAllowNull != "")
    {
        s = s + " " + sAllowNull + "";
    }
}

我们完成了 :)

通用数据库管理员操作

DB Admin in Action

文章历史

  • 2005 年 11 月 25 日:首次发布。
  • 2005 年 11 月 26 日:根据 Marc Clifton 的建议删除了 ID 约束。参见上面的假设。新假设:每个表有一个主键,它是表中的第一个字段
  • 2005 年 11 月 29 日:添加了AdminLite - 无需登录、无需管理员表、无需自定义。只需添加您的连接字符串,它就可以使用了,并且只有一个假设:每个表有一个主键,它是表中的第一个字段
  • 2005 年 12 月 1 日:修复了 C K Maxjamesxd 报告的由第一次增强引入的多个分页和排序错误 - 多谢你们两人 :)。
  • 2005年12月11日
    • 根据 jonny0 的建议,为表添加了创建/读取/更新/删除和截断功能。
    • 根据 mkrawats 的建议,根据自定义查询过滤记录。
    • 在数据库上执行自定义 SQL 语句。
  • 2006 年 3 月 23 日
    • 根据 Crap bag 的建议,为登录页添加了 SQL 注入解决方法。
    • 添加了自动插入日期/时间以添加新记录。
  • 2006 年 4 月 12 日:支持多个图像(SQL 表中的二进制数据)。

以及感谢

感谢您一直以来的支持!作为我的第一篇文章,我之前曾非常怀疑 [^]。非常感谢所有审阅并认为它有用的人,当然,也感谢你们提出的精彩建议和评论。

希望您觉得有用,如果您觉得有用,请给我投票/评论,并保重。

© . All rights reserved.