异步数据库调用 MySql 数据库出错

Asynchronous Database call to MySql Database gives error

我正在使用 Microsoft.Enterprise 库和 MySql 数据库。 我正在尝试异步调用数据库方法。

我的方法如下..

public static async Task<DataTable> ExecuteDataSetWithParameterAsSourceAsync(this Database database, CommandDetail cmdDetail, params Object[] parameterSource)
    {
        List<int> outParamIndex = new List<int>();

        AsyncCallback cb = new AsyncCallback(EndExecuteReaderCallBack);

        DataTable dt = new DataTable();
        DbAsyncState state = BeginExecuteReader(database, cmdDetail.CommandText, cb, parameterSource);
        IDataReader reader = (IDataReader)state.State;
        dt.Load(reader);
        reader.Close();
      ...
        return await Task.FromResult(dt);
    }

我低于错误

{“数据库类型“MySqlDatabase”不支持异步操作。”}

下面是错误的完整堆栈图像..

我的连接字符串是

 <add name="VirtualCloudDB" providerName="EntLibContrib.Data.MySql" connectionString="database=test;uid=xxx;password=xxx;Data Source=test-instance; maximumpoolsize=3"/>

关于错误

Oracle 的连接器/.NET 库在 v8.0 之前甚至不允许异步操作。即使是现在,也有几个怪癖。最好使用独立的开源 MySqlConnector 库。

如果您绝对必须使用 Connector/.NET,请升级到最新版本。

关于代码(无历史课)

忘记 EntLib,尤其是 DAAB。甚至文档也说:

The Database class leverages the provider factory model from ADO.NET. A database instance holds a reference to a concrete DbProviderFactory object to which it forwards the creation of ADO.NET objects.

无论如何,您使用的不是真实的东西,它是 official code that used to be stored in Codeplex. The only thing that is still in development is the Unity DI container.

的社区支持的克隆

真正的异步操作在 ADO.NET 中可用并由大多数提供商实现。与数据库无关的 EntLib 1 factory-based model 早在 2006 年就被并入了 ADO.NET 2。Entlib 2.0 DAAB 本质上是 ADO.NET 2.ADO.NET 之上的一层薄薄的便利方法。

ADO.NET 2“原始”

单独在ADO.NET 2.0中,整个方法可以替换为:

async Task<DataTable> LoadProducts(string category)
{
    var sql="select * from Products where category=@category";
    using(var connection=new MySqlConnection(_connStrFromConfig))
    using(var cmd=new MySqlCommand(sql,connection))
    {
        cmd.Parameters.AddWithValue("@category",category);

        await connection.OpenAsync();

        using(var reader=await cmd.ExecuteReaderAsync())
        {
            DataTable dt = new DataTable();
            dt.Load(reader);
            return dt;
        }
    }
}

特别是 MySQL,最好使用开源 MySqlConnector 库,而不是 Oracle 的官方 Connector/.NET。

ADO.NET 2 工厂模式

ADO.NET 2 添加了抽象基础 classes 和工厂模型(基于 DAAB 1,但更简单),允许尽可能多地使用与数据库无关的代码。

之前的代码,没有使用提供者工厂,可以重写为:


string _providerName="MySqlConnector"


DbCommand CreateConnection()
{
    DbProviderFactory _factory =DbProviderFactories.GetFactory(_providerName);
    connection = _factory.CreateConnection();
    connection.ConnectionString = connectionString;
    return connection;
}

async Task<DataTable> LoadProducts(string category)
{
    var sql="select * from Products where category=@category";
    using(DbConnection connection=CreateConnection())
    using(DbCommand cmd= connection.CreateCommand())
    {
        cmd.CommandText=sql;
        var param=cmd.CreateParameter();
        param.Name="@category";
        //The default is String, so we don't have to set it
        //param.DbType=DbType.String;
        param.Value=category;
        
        cmd.Parameters.Add("@category",category);

        await connection.OpenAsync();

        using(var reader=await cmd.ExecuteReaderAsync())
        {
            DataTable dt = new DataTable();
            dt.Load(reader);
            return dt;
        }
    }
}

如 SQL 服务器或 Oracle 正在注册并使用不同的提供程序名称。

代码可以简化。例如,DbParameterCollection.AddRange 可用于一次添加多个参数。不过,按照现代标准,这仍然是太多的代码。

Entlib 2 DAAB - 相同 classes

Entlib 2 DAAB 使用相同的摘要 classes。事实上,Database class 只是在抽象 class 之上添加方便的方法,例如创建 DbCommand 或执行查询的方法和 return reader 或数据集。

如果你不需要参数,你可以这样写:

DataTable LoadProducts(Database database)
{
    var sql="select * from Products";
    var set=database.ExecuteDataSet(CommandType.Text,sql);
    return set.Tables[0];
}

遗憾的是,无法合并原始查询和参数。在创建 EntLib 1 时,人们认为复杂的代码应该始终存储在存储过程中。因此,虽然有 ExecuteDataSet(string storedProcedureName,params object[] parameterValues),但没有原始 SQL.

的等价物

也没有基于任务的异步方法。到 2010 年,EntLib 已经处于支持模式。

再次不幸无法直接从数据库创建 DbCommand。同样,假设人们将执行原始 SQL 或调用存储过程。有一个 GetSqlStringCommand 不接受任何参数。还有 Database.ProviderFactory 可用于手动执行所有操作,并最终得到与原始 ADO.NET.

相同的代码

另一种可能的选择是作弊,使用带有位置参数的 Database.GetStoredProcCommand 并更改 CommandType

async Task<DataTable> LoadProducts(Database database,string category)
{
    var sql="select * from Products where category=@category";
    using(var cmd=database.GetStoredProcCommand(sql,category))
    {
        cmd.CommandType=CommandType.Text;
        using(var reader=await cmd.ExecuteReaderAsync())
        {
            DataTable dt = new DataTable();
            dt.Load(reader);
            return dt;
        }
    }

    return set.Tables[0];
}

小巧玲珑

使用像 Dapper 这样的 microORM 库,代码可以简化为:

async Task<IEnumerable<Product>> LoadProducts(string category)
{
    var sql="select * from Products where category=@category";
    using(var connection=CreateConnection())
    {

        var products=await connection.Query<Product>(sql,new {category=category});

        return products;
    }
}

如果连接关闭,Dapper 将打开连接,异步执行查询并将结果映射到目标对象,只需一行代码。参数将从参数对象按名称映射。

在没有类型参数的情况下调用时,Query return 是 dynamic 个对象的列表

async Task<IEnumerable<dynamic>> LoadProducts(string category)
{
    var sql="select * from Products where category=@category";
    using(var connection=CreateConnection())
    {

        var products=await connection.Query(sql,new {category=category});

        return products;
    }
}