2

我想使用 MySQL.Data 连接器查询我的 MySQL 数据库。我知道我可以使用实体框架或其他类似于 ORM 的便捷工具,例如 Dapper。但我想尝试“原生”方式。首先,我创建了一个处理查询执行的基础存储库

public abstract class BaseRepository
{
    private async Task<MySqlConnection> GetDatabaseConnection()
    {
        string databaseConnectionString = "connection string";
        MySqlConnection mySqlConnection = new MySqlConnection(databaseConnectionString);

        await mySqlConnection.OpenAsync();

        return mySqlConnection;
    }
    
    private async Task<DbDataReader> Execute(string commandText, Dictionary<string, object> parameterValues)
    {
        await using MySqlConnection mySqlConnection = await GetDatabaseConnection();
        
        MySqlCommand mySqlCommand = new MySqlCommand()
        {
            CommandText = commandText,
            Connection = mySqlConnection
        };

        foreach (KeyValuePair<string, object> parameterValue in parameterValues)
        {
            mySqlCommand.Parameters.AddWithValue(parameterValue.Key, parameterValue.Value);
        }
        
        await mySqlCommand.PrepareAsync();
        
        return await mySqlCommand.ExecuteReaderAsync();
    }
    
    protected async Task<TResult> Read<TResult>(string commandText, Dictionary<string, object> parameterValues, Func<DbDataReader, Task<TResult>> action)
    {
        DbDataReader dbDataReader = await Execute(commandText, parameterValues);
        
        return await action(dbDataReader);
    }
    
    protected async Task<int> Write(string commandText, Dictionary<string, object> parameterValues)
    {
        DbDataReader dbDataReader = await Execute(commandText, parameterValues);

        return dbDataReader.RecordsAffected;
    }
}

如您所见,有一个ReadandModify方法。由于 MySQL 无法返回插入/更新的行(就像 Postgres 那样),我只返回受影响的行数以进行更新和删除。我知道基础存储库还不支持事务,但暂时忽略它。下面的例子演示了用法

    public Task<User> GetUserAsync(string username)
    {
        return Read<User>(
            "SELECT * FROM person WHERE username = @username", 
            new Dictionary<string, object>()
            {
                { "username", username }
            },
            async dbDataReader =>
            {
                try
                {
                    bool recordFound = await dbDataReader.ReadAsync();
                    
                    if (!recordFound)
                    {
                        return null;
                    }
                    
                    // ...
                }
                catch (Exception e)
                {
                    throw;
                }
            });
    }

问题是当调试器命中await dbDataReader.ReadAsync()我得到这个异常

MySql.Data.MySqlClient.MySqlException (0x80004005):阅读器关闭时尝试读取无效。

为什么阅读器过早关闭?我该如何解决?


更新

正如评论中已经提到的,原因是await using MySqlConnection mySqlConnection = await GetDatabaseConnection();在调用方法结束时关闭了连接。

我可以在每个存储库方法中处理整个阅读器逻辑,但我想避免在每个存储库方法中重复自己。

有没有办法可以重新设计BaseRepository方法,以便它们不会过早关闭连接?

4

2 回答 2

2

我认为@Question3r 的答案应该没问题,但这是一个更短的解决方案。该Read方法是多余的,并且该Write方法运行与运行相同的逻辑Read。应该不再需要它们了,相关的部分是Execute方法。

protected async Task<TResult> Execute<TResult>(string commandText, Dictionary<string, object> parameterValues, Func<DbDataReader, Task<TResult>> action)
{
    // ... logic remains the same ...

    return await action(dbDataReader);
}

protected async Task<int> Execute(string commandText, Dictionary<string, object> parameterValues)
{
    return await Execute<int>(commandText, parameterValues, dbDataReader => Task.FromResult(dbDataReader.RecordsAffected));
}
于 2020-07-08T07:56:36.350 回答
1

所以我想我找到了一个适合我的解决方案。我必须使用回调并Execute在关闭阅读器之前调用回调。所以更新的代码将是

private async Task<TResult> Execute<TResult>(string commandText, Dictionary<string, object> parameterValues, Func<DbDataReader, Task<TResult>> action)
{
    await using MySqlConnection mySqlConnection = await GetDatabaseConnection();
        
    MySqlCommand mySqlCommand = new MySqlCommand()
    {
        CommandText = commandText,
        Connection = mySqlConnection
    };
    
    foreach (KeyValuePair<string, object> parameterValue in parameterValues)
    {
        mySqlCommand.Parameters.AddWithValue(parameterValue.Key, parameterValue.Value);
    }
        
    await mySqlCommand.PrepareAsync();
        
    DbDataReader dbDataReader = await mySqlCommand.ExecuteReaderAsync();

    return await action(dbDataReader);
}
    
protected async Task<TResult> Read<TResult>(string commandText, Dictionary<string, object> parameterValues, Func<DbDataReader, Task<TResult>> action)
{
    return await Execute(commandText, parameterValues, action);
}
    
protected async Task<int> Write(string commandText, Dictionary<string, object> parameterValues)
{
    return await Execute(commandText, parameterValues, dbDataReader => Task.FromResult(dbDataReader.RecordsAffected));
}

你怎么看待这件事?高度赞赏改进:)

于 2020-07-08T05:11:33.643 回答