使用C#中的BeginExecuteReader设计异步数据提供程序

时间:2016-08-25 16:05:09

标签: c# async-await

我是C#5中async / await模型的新手,所以你必须对我的无知/缺乏理解保持耐心。

我想升级我的Singleton设计数据提供程序以执行对存储过程的异步调用,然后使用System.Data命名空间中的BeginDataReader和EndDataReader方法返回数据。

以下是我尝试构建的结构示例,但它不是在等待数据返回:

public class DataProvider{
    private static DataProvider instance;
    public static DataProvider Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (typeof(DataProvider))
                    {
                        instance = new DataProvider();
                    }
                }
                return instance;
            }
        }

    public virtual async void ExecuteDataReaderAsync(string StoredProcedureName, AsyncCallback callback, params object[] Parameters)
        {
            InitDatabase();
            var connection = new SqlConnection(databaseControllers[connectionStringName].ConnectionString);
            var cmd = new SqlCommand();

            cmd.Connection = connection;
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = dbPrefixName + StoredProcedureName;
            await connection.OpenAsync();

            SqlCommandBuilder.DeriveParameters(cmd);
            if (cmd.Parameters.Count - 1 > Parameters.Count())
                throw new InvalidOperationException("The number of parameters provided does not match the number of parameters in the stored procedure. There are " + Parameters.Count().ToString() + " parameters provided, however the stored procedure requires " + cmd.Parameters.Count.ToString() + " parameters.");

            for (int i = 0; i < Parameters.Count(); i++)
            {
                cmd.Parameters[i + 1].Value = Parameters[i];
            }

            cmd.BeginExecuteReader(new AsyncCallback(callback), cmd);
        }
}

public class SubDataProvider : DataProvider
{
    private static volatile SubDataProvider instance = new SubDataProvider();
        public static SubDataProvider Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (typeof(SubDataProvider))
                    {
                        if (instance == null)
                            instance = new SubDataProvider();
                    }
                }
                return instance;
            }
        }

    ////
    //// THIS IS WHERE I GET LOST
    ////

        public async Task<List<Models.MyData>> GetDataAsync(bool IncludeDeleted = false)
        {
            List<Models.MyData> temp = new List<MyData>();
            ExecuteDataReaderAsync("GetData", delegate (IAsyncResult result)
            {

                var database = (SqlCommand)result.AsyncState;

                using (IDataReader reader = database.EndExecuteReader(result))
                {
                    while (reader.Read())
                    {
                        temp.Add(FillData(reader));
                    }

                }

                if (database.Connection.State == ConnectionState.Open)
                    database.Connection.Close();

            }, false);
            return temp;
        }
}


public class BusinessController
{
        private static volatile  BusinessController _instance = new BusinessController();

        public static BusinessController Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (typeof (BusinessController))
                    {
                        _instance = new BusinessController();
                    }
                }
                return _instance;
            }
        }

        public async Task<List<Models.MyData>> GetAllAsync(bool IncludeDeleted = false)
        {
            return await SubDataProvider.Instance.GetDataAsync(IncludeDeleted);;
        }
}


// DEMO
internal class Program
{
        private static void Main(string[] args)
        {
            var x = BusinessController.Instance.GetAllAsync(false);
        }
}

我的最终目标是将这些数据恢复到异步WebApi,但我目前仍然没有收到任何数据。 temp变量被填充但它实际上从未返回填充的对象。

我哪里错了?

提前非常感谢你!

1 个答案:

答案 0 :(得分:2)

我没有确切的代码库,但这样的内容将与您的需求接近:

内部课程DataProvider我将ExecuteDataReaderAsync重命名为GetDataReaderAsync

public virtual async Task<IDataReader> GetReaderAsync(string StoredProcedureName, params object[] Parameters)
{
    InitDatabase();
    var connection = new SqlConnection(databaseControllers[connectionStringName].ConnectionString);
    var cmd = new SqlCommand
    {
        Connection = connection,
        CommandType = CommandType.StoredProcedure,
        CommandText = dbPrefixName + StoredProcedureName
    };

    await connection.OpenAsync();

    SqlCommandBuilder.DeriveParameters(cmd);
    if (cmd.Parameters.Count - 1 > Parameters.Count())
        throw new InvalidOperationException("The number of parameters provided does not match the number of parameters in the stored procedure. There are " + Parameters.Count().ToString() + " parameters provided, however the stored procedure requires " + cmd.Parameters.Count.ToString() + " parameters.");

    for (int i = 0; i < Parameters.Count(); i++)
    {
        cmd.Parameters[i + 1].Value = Parameters[i];
    }

    var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection);
    return reader;
}

并在GetDataAsync内的SubDataProvider

中使用了读者
public async Task<List<MyData>> GetDataAsync(bool IncludeDeleted = false)
{
    List<MyData> temp = new List<MyData>();
    using (var reader = await GetReaderAsync("GetData"))
    {
        while (reader.Read())
        {
            temp.Add(FillData(reader));
        }
    }
    return temp;
}

我无法使用此处的内容测试解决方案,但如果您选择发回SqlDataReader而不是IDataReader,则您有{{1}你也可以使用方法。但是既然你现在已经有了异步方法,那么使用ReadAsync()就可以了。我抛弃了回调,因为在这里继续使用Task-Asynchronous Paradigm会更好。

希望这会有所帮助。 :)

更新

来自@Scott张伯伦的评论中,另一个建议是让读者保持reader.Read()级而不是DbDataReader,这样你仍然可以是通用的,并且可以访问像{{1}这样的方法}。这个建议被认为是值得的,因此在这里添加了答案。