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

时间:2016-11-16 21:46:02

标签: c# asp.net-mvc linq asynchronous task-parallel-library

我想循环遍历db连接字符串的集合并对所有DB执行查询,然后将每个IEnumberable结果合并到一个IEnumerable List中。同步版本如下所示:

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()但我收到有关投射非匿名类型的错误。它可以在没有异步任务的情况下编写,并且仍然可以跨所有连接字符串同时执行查询吗?如果它可以以任何一种方式完成,那么一种方式更好或更差。

问题的另一部分是(2)ListSites()在编写并发DB调用时应该如何看待?我看过this example,但它并没有让我找到一个有效的解决方案。我曾经玩过这样的事情:

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);

......但是无法正常工作。每个DB调用都返回一个List。当所有任务的结果合并到变量lstSites(即视图模型)时,问题/问题就会得到解决/回答。

2 个答案:

答案 0 :(得分:0)

这里有2个答案

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

谈到异步时.. 你需要的第一件事是使你的方法GetSitesForInstance异步。 要做到这一点

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

2-ND  在调用方法时 - 在它前面添加'await'。

答案 1 :(得分:0)

这是使这项工作的代码:

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()正在等待