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

MySqlExpress - 将 MySQL 的行转换为类对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (6投票s)

2023年1月8日

CPOL

3分钟阅读

viewsIcon

10632

downloadIcon

168

将 MySQL 行转换为 C# 类对象。通过传递类或字典执行 INSERT/UPDATE

引言

MySqlExpress 自动将 MySql 表中的行转换为类对象。

这个类库存在的目的是为了增强和改进 C# 中处理 MySQL 数据的传统方式。

背景

让我们看一下 C# 中处理 MySQL 数据的典型传统方法。

这是一个常见的 MySQL 表的示例

CREATE TABLE `book` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `barcode` VARCHAR(45),
  `title` VARCHAR(300),
  `author` VARCHAR(300),
  `publisher` VARCHAR(300),
  `date_register` DATETIME,
  `price` DECIMAL(12,2),
  PRIMARY KEY (`id`)
) ENGINE = InnoDB;

以下是一种获取数据的原始方法:将数据加载到 DataTable

using MySqlConnector;

DataTable dt = new DataTable();

using (MySqlConnection conn = new MySqlConnection(ConnString))
{
    using (MySqlCommand cmd = new MySqlCommand())
    {
        cmd.Connection = conn;
        conn.Open();

        cmd.CommandText = "select * from book where id=@id";
        cmd.Parameters.AddWithValue("@id", 1);

        MySqlDataAdapter da = new MySqlDataAdapter(cmd);
        da.Fill(dt);

        conn.Close();
    }
}

然后,DataTable 将直接用于填充应用程序表单输入

DataRow dr = dt.Rows[0];

lbId.Text = dr["id"] + "";
txtBarcode.Text = dr["barcode"] + "";
txtTitle.Text = dr["title"] + "";
txtAuthor.Text = dr["author"] + "";
txtPublisher.Text = dr["publisher"] + "";

DateTime dateRegister = Convert.ToDateTime(dr["date_register"]);
txtDateRegister.Text = dateRegister.ToString("yyyy-MM-dd");

decimal price = Convert.ToDecimal(dr["price"]);
txtPrice.Text = price.ToString("0.00");

或者加载到类对象(数据映射)中,以便重用并享受 IDE IntelliSense 在获取数据字段时的优势

// creates a class
public class Book
{
    public int Id { get; set; }
    public string Barcode { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public string Publisher { get; set; }
    public DateTime DateRegister { get; set; }
    public decimal Price { get; set; }
}

// declare a book object
Book book = new Book();

// fill data from DataTable
book.Id = Convert.ToInt32(dr["id"]);
book.Barcode = dr["barcode"] + "";
book.Title = dr["title"] + "";
book.Author = dr["author"] + "";
book.Publisher = dr["publisher"] + "";
book.DateRegister = Convert.ToDateTime(dr["date_register"]);
book.Price = Convert.ToDecimal(dr["price"]);

然后,该类可以这样重用

txtBarcode.Text = book.Barcode;
txtTitle.Text = book.Title;
txtAuthor.Text = book.Author;
txtPublisher.Text = book.Publisher;
txtDateRegister.Text = book.DateRegister.ToString("yyyy-MM-dd");
txtPrice.Text = book.Price.ToString();

在某些时候,开发人员会很快意识到这种方法的问题。它需要手动输入大量的字段名称(或列名称)。这是重复且繁琐的输入。很容易引入人为的输入错误。

因此,自动映射的想法很快就会出现。

转换工作从检索类对象的字段和属性开始。

在 C# 中,我们可以使用 System.Reflection 来获取它

using System.Reflection;

// get all fields
var fields = typeof(Book).GetFields(BindingFlags.NonPublic |
               BindingFlags.Public | BindingFlags.Instance);

// get all properties
var properties = typeof(Book).GetProperties(BindingFlags.NonPublic |
               BindingFlags.Public | BindingFlags.Instance);

然后,我们可以循环遍历 DataTable 中的所有字段、属性和 DataColumn,以匹配字段/属性/列的名称。一旦它们匹配,数据就可以填充到类中的特定字段/属性中。

DataTable dt = GetBookFromMySql();
DataRow dr = dt.Rows[0];

Book book = new Book();

// loop through all the fields
foreach (var fieldInfo in fields)
{
    foreach (DataColumn dc in dt.Columns)
    {
        // field name matches with column name (MySQL)
        if (fieldInfo.Name == dc.ColumnName)
        {
            // extract value from MySql
            object value = GetValue(dr[dc.ColumnName], fieldInfo.FieldType);

            // fill data (map data)
            fieldInfo.SetValue(book, value);

            break;
        }
    }
}

foreach (var propertyInfo in properties)
{
    // skip property that cannot be written
    if (!propertyInfo.CanWrite)
        continue;

    foreach (DataColumn dc in dt.Columns)
    {
        // field name matches with column name (MySQL)
        if (propertyInfo.Name == dc.ColumnName)
        {
            // extract value from MySql
            object value = GetValue(dr[dc.ColumnName], propertyInfo.PropertyType);

            // fill data (map data)
            propertyInfo.SetValue(book, value);

            break;
        }
    }
}

你会注意到有一条特殊的行正在进行数据提取

// for field
object value = GetValue(dr[dc.ColumnName], fieldInfo.FieldType);

// for property
object value = GetValue(dr[dc.ColumnName], propertyInfo.PropertyType);

在某些情况下,从 MySQL 返回的数据可能与类字段或属性的目标数据类型不完全兼容。例如,NULL 值,它会导致异常。

以下是作为过滤器的额外步骤

static object GetValue(object ob, Type t)
{
    if (t == typeof(string))
    {
        return ob + "";
    }
    else if (t == typeof(bool))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return false;
        return Convert.ToBoolean(ob);
    }
    else if (t == typeof(byte))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0;
        return Convert.ToByte(ob);
    }
    else if (t == typeof(sbyte))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0;
        return Convert.ToSByte(ob);
    }
    else if (t == typeof(short))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0;
        return Convert.ToInt16(ob);
    }
    else if (t == typeof(ushort))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0;
        return Convert.ToUInt16(ob);
    }
    else if (t == typeof(int))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0;
        return Convert.ToInt32(ob);
    }
    else if (t == typeof(uint))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0;
        return Convert.ToUInt32(ob);
    }
    else if (t == typeof(long))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0L;
        return Convert.ToInt64(ob);
    }
    else if (t == typeof(ulong))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0L;
        return Convert.ToUInt64(ob);
    }
    else if (t == typeof(float))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0F;
        return Convert.ToSingle(ob);
    }
    else if (t == typeof(double))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0D;
        return Convert.ToDouble(ob, CultureInfo.InvariantCulture);
    }
    else if (t == typeof(decimal))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return 0m;
        return Convert.ToDecimal(ob, CultureInfo.InvariantCulture);
    }
    else if (t == typeof(char))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return Convert.ToChar("");
        return Convert.ToChar(ob);
    }
    else if (t == typeof(DateTime))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return DateTime.MinValue;
        return Convert.ToDateTime(ob, CultureInfo.InvariantCulture);
    }
    else if (t == typeof(byte[]))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return null;

        return (byte[])ob;
    }
    else if (t == typeof(Guid))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return null;

        return (Guid)ob;
    }
    else if (t == typeof(TimeSpan))
    {
        if (ob == null || ob.GetType() == typeof(DBNull))
            return null;

        return (TimeSpan)ob;
    }

    return Convert.ChangeType(ob, t);
}

以上所有内容都演示了数据转换(数据映射)如何工作的基本思想。

如何自动化 INSERT

对于 INSERT,我们可以使用 DICTIONARY 将列与数据匹配。例如

Dictionary<string, object> dic = new Dictionary<string, object>();

dic["barcode"] = txtBarcode.Text;
dic["title"] = txtTitle.Text;
dic["author"] = txtAuthor.Text;
dic["publisher"] = txtPublisher.Text;
dic["date_register"] = ConvertInputToDate(txtDateRegister.Text);
dic["price"] = Convert.ToDecimal(txtPrice.Text);

DICTIONARY 执行循环以构建 SQL 语句

StringBuilder sb = new StringBuilder();

sb.Append("insert into `");
sb.Append(tableName);
sb.Append("` (");

bool isFirst = true;

// build the columns string
foreach(var kv in dic)
{
    if (isFirst)
    {
        isFirst = false;
    }
    else
    {
        sb.Append(",");
    }

    sb.Append("`");
    sb.Append(kv.Key);
    sb.Append("`");
}

sb.Append(") values(");

isFirst = true;

// building values string
foreach (var kv in dic)
{
    if (isFirst)
    {
        isFirst = false;
    }
    else
    {
        sb.Append(",");
    }

    sb.Append("@");
    sb.Append(kv.Key);
}

sb.Append(");");

cmd.CommandText = sb.ToString();

// remove all parameters
cmd.Parameters.Clear();

// adding parameters
foreach (var kv in dic)
{
    cmd.Parameters.AddWithValue($"@{kv.Key}", kv.Value);
}

cmd.ExecuteNonQuery();

这将创建一个 INSERT 语句。

如何自动化 INSERTUPDATE(二合一)操作?

让我们尝试一下。

// get columns details of the table
DataTable dt = Select($"show columns from `{table}`;");

List<string> lstCol = new List<string>();
List<string> lstUpdateCol = new List<string>();

foreach (DataRow dr in dt.Rows)
{
    // collecting all column names
    lstCol.Add(dr[0] + "");

    // collecting all NON-Primary Key column names
    if ((dr["Key"] + "").ToUpper() != "PRI")
    {
        lstUpdateCol.Add(dr[0] + "");
    }
}

获取类的所有字段和属性

// get all fields
var fields = typeof(Book).GetFields(BindingFlags.NonPublic |
               BindingFlags.Public | BindingFlags.Instance);

// get all properties
var properties = typeof(Book).GetProperties(BindingFlags.NonPublic |
               BindingFlags.Public | BindingFlags.Instance);

构建用于数据收集的 DICTIONARY

Dictionary<string, object> dic = new Dictionary<string, object>();

// loop through all column names
foreach (var col in lstCol)
{
    // loop through all fields
    foreach (var field in fields)
    {
        // column name matches with field name
        if (col == field.Name)
        {
            // fill data into dictionary
            dic[col] = field.GetValue(book);
            break;
        }
    }

    // loop through all properties
    foreach (var prop in properties)
    {
        // column name matches with property name
        if (col == prop.Name)
        {
            // fill data into dictionary
            dic[col] = prop.GetValue(book);
            break;
        }
    }
}

构建 SQL 语句

StringBuilder sb = new StringBuilder();

sb.Append("insert into `");
sb.Append(table);
sb.Append("`(");

bool isFirst = true;

// build the columns string
foreach (KeyValuePair<string, object> kv in dic)
{
    if (isFirst)
        isFirst = false;
    else
        sb.Append(",");

    sb.Append("`");
    sb.Append(kv.Key);
    sb.Append("`");
}

sb.Append(") values(");

isFirst = true;

// build the values string
foreach (KeyValuePair<string, object> kv in dic)
{
    if (isFirst)
        isFirst = false;
    else
        sb.Append(" , ");

    sb.Append("@v");
    sb.Append(kv.Key);
}

// build the update statement
sb.Append(") on duplicate key update ");

isFirst = true;

// match the data with all non-primary key columns
foreach (string key in lstUpdateCols)
{
    if (isFirst)
        isFirst = false;
    else
        sb.Append(",");

    sb.Append("`");
    sb.Append(key);
    sb.Append("`=@v");
    sb.Append(key);
}

sb.Append(";");

cmd.CommandText = sb.ToString();

// remove parameters
cmd.Parameters.Clear();

// adding parameters
foreach (KeyValuePair<string, object> kv in dic)
{
    cmd.Parameters.AddWithValue("@v" + kv.Key, kv.Value);
}

// execute the INSERT UPDATE statement
cmd.ExecuteNonQuery();

这演示了 INSERT + UPDATE 如何自动化的基本思想。

Using the Code

MySqlExpress 是基于上述想法构建的。

下面介绍了一些预构建的方法,这些方法可以自动执行 SELECTINSERTUPDATE

声明一个使用 MySqlExpress 的标准代码块

using (MySqlConnection conn = new MySqlConnection(ConnString))
{
    using (MySqlCommand cmd = new MySqlCommand())
    {
        cmd.Connection = conn;
        conn.Open();

        MySqlExpress m = new MySqlExpress(cmd);

        // starts here

        conn.Close();
    }
}

获取单行(对象)

var dicParam = new Dictionary<string, object>();
dicParam["@id"] = 1;
string sql = "select * from book where id=@id";
Book book = m.GetObject<Book>(sql, dicParam);

获取多行(对象列表)

List<book> lst = m.GetObjectList<Book>("select * from book;");

保存(INSERT + UPDATE)一个类对象

// Saving single object
m.Save("book", book);

// Saving list of objects
m.SaveList("book", lstBook);

使用 DICTIONARY 保存(仅 INSERT)

Dictionary<string, object> dic = new Dictionary<string, object>();

dic["barcode"] = txtBarcode.Text;
dic["title"] = txtTitle.Text;
dic["author"] = txtAuthor.Text;
dic["publisher"] = txtPublisher.Text;
dic["date_register"] = ConvertInputToDate(txtDateRegister.Text);
dic["price"] = Convert.ToDecimal(txtPrice.Text);

m.Insert("book", dic);

使用 DICTIONARY 保存(仅 UPDATE)- 单个主键

Dictionary<string, object> dic = new Dictionary<string, object>();

dic["barcode"] = txtBarcode.Text;
dic["title"] = txtTitle.Text;
dic["author"] = txtAuthor.Text;
dic["publisher"] = txtPublisher.Text;
dic["date_register"] = ConvertInputToDate(txtDateRegister.Text);
dic["price"] = Convert.ToDecimal(txtPrice.Text);

m.Update("book", dic, "id", id);

使用 DICTIONARY 保存(仅 UPDATE)- 多个主键

// the data
var dic = new Dictionary<string, object>();
dic["publisher"] = txtPublisher.Text;
dic["date_register"] = ConvertInputToDate(txtDateRegister.Text);
dic["price"] = Convert.ToDecimal(txtPrice.Text);

// the condition
var dicCond = new Dictionary<string, object>();
dic["title"] = txtTitle.Text;
dic["author"] = txtAuthor.Text;

// updating single row
m.Update("book", dic, dicCond)
m.Update("book", dic, dicCond, true);

// updating multi rows
m.Update("book", dic, dicCond, false);

删除或执行任何 SQL 语句

m.Execute("delete from book where id=1");
m.Execute("update book set status=1;");

从第一行和第一列获取数据

string name = m.ExecuteScalar<string>("select title from book where id=1;");
int total = m.ExecuteScalar<int>("select count(id) from book;");

执行带参数的查询

var dicParam = new Dictionary<string, object>();
dicParam["@id"] = 1;

m.Execute("delete from book where id=@id;", dicParam);
string name = m.ExecuteScalar<string>("select title from book where id=@id;", dicParam);

MySqlExpress 助手

助手应用程序是 MySqlExpress 项目的一部分。它将从 MySQL 表生成类。它加载特定表的列,并将它们转换为 C# 类字段和属性。因此,它可以帮助开发人员轻松创建 C# 类。

它还可以基于列生成 DICTIONARY 条目。

有关助手如何与 MySqlExpress 一起使用的详细信息,您可以阅读[这篇文章]和[这篇文章]。

好了,这篇文章就到此为止。祝大家愉快!编码愉快!

历史

  • 2023 年 1 月 8 日:初始版本(发布 v1.7.2)
© . All rights reserved.