异步运行可变数量的LINQ查询

本文关键字:LINQ 查询 运行 异步 | 更新日期: 2023-09-27 18:18:11

我想循环通过db连接字符串的集合,并对所有db执行查询,然后将每个IEnumerable结果合并为一个IEnumerable List<T>

同步版本如下:

public ActionResult ListSites()
{
    ConnectionStringSettingsCollection ConnectionStrings = ConfigurationManager.ConnectionStrings;
    List<SiteInfoModel> lstSites = new List<SiteInfoModel>();
    
    foreach (ConnectionStringSettings cn in ConnectionStrings)
    {
        lstSites.AddRange(getSitesForInstance(cn));
    }
    return View("~/Views/Sites/List.cshtml", lstSites);
}
private List<SiteInfoModel> getSitesForInstance(ConnectionStringSettings css)
{            
    using (STContext db = new STContext(css.ConnectionString))
    {
        IEnumerable<SiteTracker.Data.site_info> sites = db.Sites;
        return (from s in sites
                     orderby s.name
                     select new SiteInfoModel
                     {
                         instance = css.Name,
                         siteName = formatValue(s.name, s.active),
                         siteUrl = formatValue(s.url, s.active),
                         siteId = s.site_id,
                         isActive = s.active
                     }).ToList();                
    }
}

这个问题可能有几个子问题,因为感觉我正在努力解决一些不同的问题:

  1. getSitesForInstance()应该怎么写?应该是

    private async Task<List<SiteInfoModel>> getSitesForInstance()
    

    如果它应该是异步的,那么我怎么写linq表达式?我尝试将IEnumerable更改为IQueryable并使用.ToListAsync(),但我得到了一个关于投射非匿名类型的错误。可以在没有async Task<T>的情况下编写,并且仍然可以跨所有连接字符串并发执行查询吗?如果两种方法都能做到,那是好还是坏呢?

  2. ListSites()在编写并发数据库调用时应该如何看待?我看过这个例子,但它并没有让我得到一个有效的解决方案。我试过这样做:

     Task<List<SiteInfoModel>>[] tasks = new Task<List<SiteInfoModel>>[ConnectionStrings.Count];
     for (int i = 0; i < ConnectionStrings.Count - 1; i++)
     {
         tasks[i] = getSitesForInstance(ConnectionStrings[i]);
     }
     Task.WaitAll(tasks);
    

    …但没能成功。每个数据库调用返回一个List<SiteInfoModel>。当所有任务的结果组合到变量lstSites中(即视图模型)时,问题就解决了。

异步运行可变数量的LINQ查询

这里有两个答案

要在一个变量中获得所有结果-您可以简单地使用Parallel。ForEachhttps://stackoverflow.com/a/12610915/444149或https://msdn.microsoft.com/en-us/library/dd460680 (v = vs.110) . aspx

当谈到async..你需要做的第一件事是使你的方法GetSitesForInstance异步。只需

  • 在方法名前添加async关键字
  • 使用.ToListAsync()在查询

2日当调用方法时-在它前面添加'await'

下面是使它工作的代码:

public async Task<ActionResult> ListAsync()
{            
     ConnectionStringSettingsCollection ConnectionStrings = ConfigurationManager.ConnectionStrings;
     List<SiteInfoModel> lstSites = new List<SiteInfoModel>();
     Task<List<SiteInfoModel>>[] tasks = new Task<List<SiteInfoModel>>[ConnectionStrings.Count];
     for (int i = 0; i < ConnectionStrings.Count; i++)
     {
         tasks[i] = getSitesForInstanceAsync(ConnectionStrings[i]);
     }
     try
     {
         Task.WaitAll(tasks.ToArray());
     }
     catch (AggregateException) { }
     for (int ctr = 0; ctr < tasks.Length; ctr++)
     {
         if (tasks[ctr].Status == TaskStatus.Faulted)
             Console.WriteLine("error occurred in {0}", ConnectionStrings[ctr].Name);
         else
         {
             lstSites.AddRange(tasks[ctr].Result);
         }
     }
     return View("~/Views/Sites/List.cshtml", lstSites);
 }
private Task<List<SiteInfoModel>> getSitesForInstanceAsync(ConnectionStringSettings css)
 {
     return Task.Run(() => getSitesForInstance(css));
 }

Visual Studio抱怨ListAsync()没有使用await关键字,但它工作得很好。这是个问题吗?似乎task。waitall()正在等待