C# 中带有 MySqlCommand.ExecuteNonQueryAsync 的 MySqlConnector 不创建记录

问题描述 投票:0回答:1

我正在使用 MySQLConnector 和 mySQL 将用 Python 编写的工作后端移植到用 C# 编写的 .NET 微服务。我有一个简单的 C# 数据库类,从 Program main 实例化作为第一行之一。它连接到 mysql 服务,然后检查数据库是否存在,并根据需要创建数据库和表,然后用一些种子记录填充它以使其可操作。

我正在尝试通过适当的错误检查和结构来做到这一点,并学习正确的做事方法。

我的问题是:

  • 我不明白如何正确构建异步代码。
  • 其中一个异步调用被阻塞并且永远不会返回
  • 我不确定如何进行异步调用来获取需要进行查询的方法的连接

下面是我当前的课程,然后是我的问题,以及下面的原始工作 Python 数据库模块。任何帮助、提示或技巧表示赞赏。

using System;
using System.Text.RegularExpressions;
using MySqlConnector;

// MySqlConnector notes and refs
// https://mysqlconnector.net/api/mysqlconnector/mysqlerrorcodetype/
// https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
// Adventureworks naming conventions: 
// https://stackoverflow.com/questions/3593582/database-naming-conventions-by-microsoft

public class Database
{
    static private Database instance;
    private bool isReady;

    private string dbName;
    private string dbUser;
    private string dbPassword;
    private string dbHost;
    private string dbPort;
    private string connectionString;

    public Database(string dbName, string dbUser, string dbPassword, string dbHost, string dbPort)
    {
        this.dbName = dbName;
        this.dbUser = dbUser;
        this.dbPassword = dbPassword;
        this.dbHost = dbHost;
        this.dbPort = dbPort;
        this.connectionString = $"server={dbHost};user={dbUser};port={dbPort};password={dbPassword}";
        instance = this;
        Initialize();
    }

    // is this the right approach for various methods in the service to get a connection?
    static public MySqlConnection Connect()
    {
        try
        {
            var connection = new MySqlConnection(instance.connectionString);
            connection.Open();

            var command = new MySqlCommand($"USE {instance.dbName}", connection);
            command.ExecuteNonQuery();

            return connection;
        }
        catch (MySqlException ex)
        {
            switch ((MySqlErrorCode)ex.Number)
            {
                case MySqlErrorCode.AccessDenied:
                    Console.WriteLine("Access denied: Something is wrong with your user name or password");
                    break;
                case MySqlErrorCode.UnknownDatabase:
                    Console.WriteLine($"Database {instance.dbName} does not exist");
                    break;
                default:
                    Console.WriteLine(ex.Message);
                    break;
            }

            Environment.Exit(1);
            return null;  // this line will never be reached, but it's necessary to satisfy the return type of the function.
        }
    }

    static public bool IsReady()
    {
        return (instance != null && instance.isReady);
    }

    //*************************************************************************
    // Private methods
    private async Task Initialize()
    {
        // create a connection and try to open the DB service
        using var connection = new MySqlConnection(connectionString);
        try
        {
            await connection.OpenAsync();
        }
        catch (MySqlException exConnection)
        {
            Console.WriteLine(exConnection.Message);
            Environment.Exit(1);
        }

        // service exists, now check if our database exists
        try
        {
            // if it got here, then it found it, need to make it active via USE
            using var command = new MySqlCommand($"USE {dbName}", connection);
            await command.ExecuteNonQueryAsync();
        }
        catch (MySqlException exUseDB)
        {
            if ((MySqlErrorCode)exUseDB.Number == MySqlErrorCode.UnknownDatabase)
            {
                Console.WriteLine($"Database {dbName} does not exist");
                Console.WriteLine($"Creating {dbName}...");
                // create the database...
                try
                {
                    // load the schema file and run the sql to create the db and tables
                    string schema = File.ReadAllText("CLIService/SQL/bbe-schema.sql");
                    string[] sqlCommands = schema.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);

                    // create the tables
                    foreach (string sqlCommand in sqlCommands)
                    {
                        string cleanedCommand = Regex.Replace(sqlCommand, "[\n\r]", "");

                        if (!string.IsNullOrEmpty(cleanedCommand))
                        {
                            try
                            {
                                var command = new MySqlCommand(cleanedCommand, connection);
                                await command.ExecuteNonQueryAsync();
                            }
                            catch (MySqlException exSqlCommand)
                            {
                                Console.WriteLine(exSqlCommand.Message);
                                Environment.Exit(1);
                            }
                        }
                    }
                }
                catch (MySqlException exCreateDB)
                {
                    Console.WriteLine($"Failed creating database: {exCreateDB.Message}");
                    Environment.Exit(1);
                }
            }
            else
            {
                Console.WriteLine(exUseDB.Message);
                Environment.Exit(1);
            }
        }

        // database exists and is used, now populate it if empty
        try
        {
            // there is always 1 admin account created by default, so try to get one record
            var command = new MySqlCommand("SELECT * FROM account LIMIT 1", connection);
            // should I use ExecuteReaderAsync() instead? If so how?
            using (var reader = command.ExecuteReader())
            {
                // if no records, means it's a new, empty database
                if (!reader.HasRows)
                {
                    // load the data file and run the sql to create the table records of initial data
                    string schema = File.ReadAllText("CLIService/SQL/bbe-data.sql");
                    string[] sqlCommands = schema.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);

                    // create the records
                    foreach (string sqlCommand in sqlCommands)
                    {
                        string cleanedCommand = Regex.Replace(sqlCommand, "[\n\r]", "");

                        if (!string.IsNullOrEmpty(cleanedCommand))
                        {
                            try
                            {
                                command = new MySqlCommand(cleanedCommand, connection);
#if USE_ASYNC_AWAIT
                                // the following line if used it hangs/blocks and never returns
                                await command.ExecuteNonQueryAsync();
#else
                                // alternatively the following line returns: "56 Inserted System.Threading.Tasks.Task`1[System.Int32] records"
                                // but no records are inserted into the DB
                                var rowCount = command.ExecuteNonQueryAsync();
                                Console.WriteLine($"Inserted {rowCount} records");
#endif
                            }
                            catch (MySqlException exSqlCommand)
                            {
                                Console.WriteLine(exSqlCommand.Message);
                                Environment.Exit(1);
                            }
                        }
                    }
                }
            }
        }
        catch (MySqlException exSelectDB)
        {
            Console.WriteLine($"An error occurred: {exSelectDB.Message}");
        }

        // database opened, selected and populated
        isReady = true;
        Console.WriteLine("Database is ready");
    }
}

问题:

  1. 我在代码的 USE_ASYNC_AWAIT 部分的 Initialize() 方法中做错了什么?当前代码成功连接到服务,检测到数据库丢失并使用 sql 命令文件创建数据库和表,检测到没有记录,加载数据文件并循环执行插入命令。在上面的代码中,如果 USE_ASYNC_AWAIT 为 true,则
    #if USE_ASYNC_AWAIT
    会阻塞并且永远不会返回,如果为 false,则会完成创建 56 个任务,但不会创建任何记录。
    Initialize 方法的整体结构是正确的做事和处理异常的方式吗?
  2. 当服务请求连接来运行查询时,应如何使用异步构建 Connect 函数?
  3. 原始工作Python后端:

await command.ExecuteNonQueryAsync

使用Database类的程序主体:

import mysql from mysql.connector import errorcode import re import os import tomllib def connect(config): # open the database connection try: connection = mysql.connector.connect(pool_name="bbepool", **config["DATABASE"]["CONNECTION"]) except mysql.connector.Error as err: if err.errno == errorcode.ER_ACCESS_DENIED_ERROR: print("Access denied: Something is wrong with your user name or password") else: print(err) exit(1) # set the active database name = config["DATABASE"]["NAME"] connection.name = name cursor = connection.cursor() try: cursor.execute("USE {}".format(name)) except mysql.connector.Error as err: print(err) exit(1) return connection def initialize(config): connection = None cursor = None # open the database connection try: connection = mysql.connector.connect(**config["DATABASE"]["CONNECTION"]) cursor = connection.cursor() except mysql.connector.Error as err: print(err) exit(1) # connection successful, check if the db exists already name = config["DATABASE"]["NAME"] try: cursor.execute("USE {}".format(name)) except mysql.connector.Error as err: print("Database {} does not exist".format(name)) if err.errno == errorcode.ER_BAD_DB_ERROR: print("Creating {}...".format(name)) try: cursor.execute("CREATE DATABASE {} DEFAULT CHARACTER SET 'utf8mb4'".format(name)) except mysql.connector.Error as err: print("Failed creating database: {}".format(err)) exit(1) print("Database {} created successfully".format(name)) # set the active database connection.database = name else: print(err) exit(1) # also set connection to use the new database config["DATABASE"]["CONNECTION"]["database"] = config["DATABASE"]["NAME"] # load and run schema # safe to do as it doesn't create tables if they exist with open(config["DATABASE"]["SCHEMA"], "rb") as f: tables = f.read().decode("utf-8").split(";") for table in tables: table = re.sub("[\n\r]", "", table) if len(table) > 0: try: cursor.execute(table) connection.commit() except mysql.connector.Error as err: print(err) exit(1) # if there is test data if config["DATABASE"]["DATA"] != "": # if the db is empty cursor.execute("SELECT * FROM account LIMIT 1") # must fetch the data for the cursor cursor.fetchone() if cursor.rowcount < 1: # populate with open(config["DATABASE"]["DATA"], "rb") as f: text = f.read().decode("utf-8") lines = text.split(");\n") for line in lines: if len(line) > 0: try: cursor.execute(line + ");") connection.commit() except mysql.connector.Error as err: print(err) exit(1) # cleanup cursor.close() connection.close() if __name__ == "__main__": # ensure the current working directory is same as script os.chdir(os.path.dirname(__file__)) # module vars we use multiple places config = {} # In VS Code we can test development or production by setting # "DEPLOYMENT_ENVIRONMENT": "development", "testing" or "production" in .vscode/launch.json # but in case the environment variable isn't set, make a default to development if "DEPLOYMENT_ENVIRONMENT" not in os.environ: os.environ["DEPLOYMENT_ENVIRONMENT"] = "development" config_file = "../Settings/config." + os.environ["DEPLOYMENT_ENVIRONMENT"] + ".toml" with open(config_file, "rb") as f: config = tomllib.load(f) # initialize database before switching directories initialize(config)

	
c# mysql-connector
1个回答
0
投票

处理数据库准备就绪的任何阶段的启动(无服务、服务但无数据库、数据库但无数据)。
  • 到处使用异步
  • Initialize() 得到了简化,因为 .NET 连接器在作为单个批处理执行 SQL 命令时不存在问题,并且它现在被组织为与数据库状态相对应的 3 个独立的逻辑块(无服务、无数据库、没有数据)
  • 获取连接被简化,因为异步连接已经被池化,因此可以简单地返回需要它的函数
  • using System; using System.Data; // https://mysqlconnector.net/tutorials/basic-api/ using MySqlConnector; using CLIShared; public class Program { static public void Main() { string dbName = Environment.GetEnvironmentVariable("DB_NAME"); string dbUser = Environment.GetEnvironmentVariable("DB_USER"); string dbPassword = Environment.GetEnvironmentVariable("DB_PASSWORD"); string dbHost = Environment.GetEnvironmentVariable("DB_HOST"); string dbPort = Environment.GetEnvironmentVariable("DB_PORT"); Database db = new Database(dbName, dbUser, dbPassword, dbHost, dbPort); // wait for db to be ready while (Database.IsReady() == false) { System.Threading.Thread.Sleep(1000); } Console.WriteLine("Database successfully initialized!"); } }
	
© www.soinside.com 2019 - 2024. All rights reserved.