【问题标题】:Asynchronous Linq Query异步 Linq 查询
【发布时间】:2019-12-15 15:16:19
【问题描述】:

我正在学习 Linq。该示例使用 Northwind 数据库。我正在尝试使以下方法异步:

public IActionResult ProductsThatCostMoreThan(decimal price)
{
    var model = db.Products
        .Include(p => p.Category)
        .Include(p => p.Supplier)
        .AsEnumerable()
        .Where(p => p.UnitPrice > price);
    return View(model);
}

如果尝试了下面的代码,但编译器抱怨IAsyncEnumerable<Product> 不包含Where 的定义,并且找不到接受IAsyncEnumerable<Product> 类型的第一个参数的可访问扩展方法“Where”。删除 where 表达式可解决此问题。如何编写异步 where 过滤器?

public async Task<IActionResult> ProductsThatCostMoreThanAsync(decimal price)
{
    var model = await db.Products
       .Include(p => p.Category)
       .Include(p => p.Supplier)
       .AsAsyncEnumerable()
       .Where(p => p.UnitPrice > price);
    return View(model);
}

尝试澄清:我将尝试更好地解释问题的上下文。这是一本书的学术练习,我正在尝试从中学习 Linq。

该方法是 Asp.net Core MVC 网站中的控制器操作方法。我了解到,如果控制器操作方法很慢(例如,它需要从数据库中获取大量数据),它会限制网站的可扩展性,因为该方法会锁定 Web 服务器线程池中的一个线程,直到它完成。使控制器中的操作方法异步将线程释放回池中,以便在方法执行缓慢的操作时可以重用它。 (见https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/october/async-programming-introduction-to-async-await-on-asp-net)。

例如:

public IActionResult Index()
{
    var model = new HomeIndexViewModel
    {
        Categories = db.Categories.ToList()
    };
    return View(model);
}

为此更改为以下内容:

public async Task<IActionResult> Index()
{
    var model = new HomeIndexViewModel
    {
         Categories = await db.Categories.ToListAsync(),
    };
    return View(model);
}

我正在尝试对问题顶部的方法进行类似的更改。

【问题讨论】:

  • 为什么要将所有数据加载到内存中?使用这个db.Products... .Where(p =&gt; p.UnitPrice &gt; price).AsEnumerable()
  • 请看我上面修改过的问题。

标签: c# asp.net-mvc entity-framework linq


【解决方案1】:

Async 在你实际做某事时出现。

只要您正在设置查询,从 task 的角度来看,使事情异步是没有意义的。

我给你举个例子;

设置查询

以下代码是关于设置查询的:

var query = db.Products
    .Include(p => p.Category)
    .Include(p => p.Supplier)
    .Where(p => p.UnitPrice > price)
    .OrderBy(c => c.SomeProperty)
    .Etc...;

在您实际枚举结果之前,不会执行上述查询。因此;在这一点上,async Task 不相关(好吧,在 99.99999% 的情况下)。


执行异步

那么你什么时候使用Taskasync如果你喜欢)

在具体化查询时使用它; IE。;执行/枚举它:

最著名的例子是这样的:

var result = await query.ToListAsync();

顺便说一句,这就是为什么你有更多这样的助手:

  • 单异步,
  • 第一异步,

问题:

如何编写异步 where 过滤器?

您很可能不需要。您希望异步执行过滤器,即;访问您的数据库或其他来源。

在具体化的时候,即; ToListAsync-ing,您希望将其作为Task 执行。

【讨论】:

  • 请看我上面修改过的问题。我认为,也许是错误的, AsEnumerable() 调用了急切加载。如果我将 AsEnumerableAsync() 更改为 ToListAsync(),where 子句仍然无法编译。
  • 嗨,从技术上讲,这将是一个不同的答案。在这个答案中,我解释了为什么构建查询对性能的影响可以忽略不计,而执行查询会导致工作负载。如果您的问题是为什么您的代码无法编译,最好提出一个新问题;在那个话题上非常具体。您的问题将得到新的关注,并且更有可能得到回答;-)
【解决方案2】:

我假设您在这里使用 EntityFamework。在这种情况下(正如其他答案指出的那样),您可以像这样选择.ToListAsync()

public async IActionResult ProductsThatCostMoreThan(decimal price)
{ 
    var model = await db.Products
                               .Include(p => p.Category)
                               .Include(p => p.Supplier)
                               .Where(p => p.UnitPrice > price).ToListAsync();
     return View(model); 
}

你必须在你的链末尾有它的原因是因为它返回Task,而不是 IEnumerable,所以你必须等待它。从技术上讲,您可以在没有过滤器的情况下枚举您的数据源,等待它,然后应用 .Where。但是 EF 的工作方式会将您的 .Where 子句编译为 SQL 代码,因此它必须是原始方法链的一部分

还有一点要说明的是,此扩展方法是 EF 程序集的一部分,因此如果您不使用 EF,您可能不得不寻找替代方法。正如其他答案指出的那样,实际查询执行发生在您调用枚举 IEnumerable 时,因此您可以在技术上编写具有类似效果的自己的扩展方法(最简单的是检查 EF 的扩展源)

【讨论】:

  • 谢谢,这行得通。我之前尝试过,但查看页面因无法翻译错误而失败。我正在使用 Sqlite,查看页面问题涉及产品类属性 UnitPrice。 UnitPrice 在模型中被声明为十进制类型。 Ling 和 Sqlite 不喜欢这样。当我将 UnitPrice 更改为 double 类型时,一切正常。
  • 我刚刚注册了stackoverflow。当我尝试对您的答案进行投票时,我了解到我的“声誉”级别还不允许这样做。再次感谢您的回答和其他人提供的答案..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-06
  • 1970-01-01
  • 2013-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多