【问题标题】:How can I return a Task<List<Model>> from dapper for "Select * from table" without having to query the resulting ids from the first query again?如何从 dapper 返回 Task<List<Model>> 以获取“从表中选择 *”,而不必再次从第一个查询中查询结果 id?
【发布时间】:2021-06-12 07:12:07
【问题描述】:

我正在开发一个 blazor 服务器端项目。 我为我的查询编写了一个存储库模式类,但函数Task&lt;List&lt;Model&gt;&gt; GetAllModelsAsync() 遇到了一些问题。我希望它返回一个任务,这样我就可以在我的部分组件类中等待它的结果,以实现更灵敏的渲染。

我使用以下代码让它工作:

public Task<List<Model>> GetAllModelsAsync()
        {
            List<Model> modelList = new();
            var sql = "SELECT * FROM model";
            var query = DbConnection.QueryAsync<Model>(sql).Result;
            
            var result = query.ToList();
            foreach (var model in result) modelList.Add(GetModelAsync(int.Parse($"{model.Id}")).Result);

            return Task.FromResult(modelList);
        }

即使它正在运行,但感觉并不是最好的版本 因为我已经读过以下语法应该有效:

using (var connection = new SqlConnection(...)) 
{ 
  var authors = connection.Query<Author>(
    "Select * From Author").ToList(); 
}

我注意到以下语法也可以正常工作:

var query = (await DbConnection.QueryAsync<Model>(sql)).ToList();

虽然我现在必须将方法更改为异步。

我试图为它定义一个任务,但无法让它工作。

var sql = "SELECT * FROM model";
var task = new Task<List<Model>>(async () =>
{
  (await DbConnection.QueryAsync<Model>(sql)).ToList();
});

return task

现在我得到return type of an 'async' anonymous function must be a 'void', 'Task', 'Task&lt;T&gt;', a task-like type, 'IAsyncEnumerable&lt;T&gt;', or 'lAsyncEnumerator&lt;T&gt;'

大多数错误状态的答案我必须返回某种类型的任务<..>但我确实返回了它。

我什至不确定这是否会破坏我的预期逻辑。 提前致谢!关于性能,我什至不确定我的第一个解决方案必须经常运行 GetSkill 是否有任何好处。

【问题讨论】:

    标签: c# dapper blazor-server-side


    【解决方案1】:

    我认为最直接的答案应该是:

    public async Task<List<Model>> GetAllModelsAsync()
        {
            var sql = "SELECT * FROM model";
            var result = (await DbConnection.QueryAsync<Model>(sql))).ToList();
            
            return result;
        }
    

    我不能说我有很多 Dapper 经验,但在您使用的初始实现中:

    1. .Result -> .Result 表示你要阻塞并等待任务完成以获取查询结果,以便立即使用。使用 await 关键字代替(这意味着还必须将 async 关键字添加到方法定义中)意味着当您正在等待的任何异步方法正在执行时,您不会阻塞线程并且当前正在执行的线程可以同时用于其他事情,直到结果准备好。我建议 Steven Cleary's blog posts 关于 async await 作为一个很好的参考。

    2. 我认为你不需要 foreach 循环。您在第一次调用时就获得了所有模型,因此您不需要基于每个 Id 重新查询它们?

    然后你会像这样调用这个方法

    var allModels = await GetAllModelsAsync();
    

    【讨论】:

    • 我真的不明白为什么这个人会做任何类型的 .Select() 或其他 Linq 的事情。他们应该能够只运行查询并返回结果。
    • @Bennyboy1973 是的,我认为这是我对 dapper 工作原理的误解/缺乏知识,正如我在答案中提到的那样。我会删除那个位
    • 我实际上只使用过 Dapper,所以我和你一样不知道有什么区别。我认为您可以删除List&lt;Model&gt; modelList = new();
    • @Bennyboy1973 解释只是因为:使用实体框架,没有自动实体映射,并且您不提供带有实际 sql 字符串的查询(通常)。因此,作为查询的一部分,您提供一个 .Select 函数,该函数限制查询中选择的字段(查询的 select x,y,z 部分),然后可以手动映射到我之前解释的对象(现在删除的位)
    • 啊,是的,我现在想起来了。当我开始使用 Blazor 时,我对 EF 感到非常兴奋。我当时研究了 IAmTimCorey 的一个视频教程系列,他推荐了 Dapper。我还没有真正看到有必要回去,尽管我已经看到最近有了很大的改进,而且 .net 6 还会有更多改进。
    【解决方案2】:

    这就是我使用 Dapper 的方式。效果很好:

    1. 制作您的 Dapper 方法 async
    2. 将调用您的方法的方法或事件也设为async Task,以便您可以await 任务。
    3. 要返回列表,请执行return (await blah blah blah).ToList();
    4. 使用所有 SQL 调用的 Async 版本。

    你的 foreach 循环似乎有点不对劲。你介意解释一下你想用它实现什么吗?您的第一个查询似乎应该返回您需要的所有模型。

    例子:

    public async Task<List<ImageChoiceQuestion>> GetImageChoiceQuestionsByCreator(string AddedByUserId)
    {
        string connectionString = _config.GetConnectionString("DefaultConnection");
        using (IDbConnection connection = new SqlConnection(connectionString));
        return (await connection.QueryAsync<ImageChoiceQuestion>(QImage.GetImageChoiceQuestionsByCreator, new { AddedByUserId })).ToList();
    }
    

    QImage.GetImageChoiceQuestionsByCreator 只是一个查询字符串。

    【讨论】:

    • 好的,谢谢,我马上试试。我的 for each 只是从第一个查询中读出所有 id,查询 id 并将其添加到结果中。我也觉得有点奇怪。
    • 当您执行该查询时,所有行的内容都已经存在于您的第一个结果集中。我建议使用var results = (await blah blah blah).ToList(); return results; 这样,如果您在 Dapper 查询之后设置断点,您可以将鼠标悬停在 results 上以弹出显示完整列表的弹出窗口。
    • 这个例子很好用,但是当我尝试将它用于我的其他功能时,我得到Exit code is 134 (Unhandled exception. Unhandled exception. System.AggregateException: One or more errors occurred. (BeginExecuteNonQuery requires an ... 因为我在UpdateSkillAsync 中使用GetSillAsync,似乎他不想要我打开一个新的连接。我认为 QueryMultiple 在这里不起作用,因为我在不同的功能中打开它。
    • 我在每个方法中创建了一个新的using connection。我将编辑我的答案以从我的代码中举一个例子。
    • 我使用this pattern from the first answer 将连接内容从我的代码中剥离出来。我实际上只是注意到我不应该使用 Task.FromResult at all 这在这里造成了一些麻烦。我只需要先查询我的更新,看看它是否已经存在,所以我不能一次又一次地添加数据。
    猜你喜欢
    • 2014-05-19
    • 1970-01-01
    • 2016-11-14
    • 2012-07-13
    • 2013-03-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-16
    • 2023-03-23
    相关资源
    最近更新 更多