使用Async/Await和EntityFramework调用多个存储过程

10

我需要调用多个耗时的存储过程。理想情况下,这些存储过程应该同时执行,但这会引发很多问题。

以下是简化后的代码:

private  async void refresh_Controle(object sender, RoutedEventArgs e)
{
    SqlParameter param1 = new SqlParameter("@devicename", DeviceName);
    Task<int> mcResult = GenkaiBase.Database.ExecuteSqlCommandAsync("exec Refresh_McAfee @devicename", param1);
    int Mc = await mcResult;

    SqlParameter param2 = new SqlParameter("@devicename", DeviceName);

    Task<int> dcaiResult = GenkaiBase.Database.ExecuteSqlCommandAsync("exec Refresh_DCAI @devicename", param2);
    int Dc = await dcaiResult;
}

以下有两个问题:

  1. 这些过程一个接一个地执行。
  2. 如果我调用这个过程超过一次,我会得到一个 SQL Server 错误,其中一个过程被选为受害者。

我尝试使用以下代码在异步方法中同时调用这两个过程:

public async Task<bool> Refresh_Control(string devicename)
{
    List<Task> Tlist = new List<Task>();
    Console.WriteLine("Launch Refresh");

    SqlParameter param1 = new SqlParameter("@devicename", devicename);

    Task<int> mcResult =  Genkai_db.Database.ExecuteSqlCommandAsync("exec Refresh_McAfee @devicename", param1);

    SqlParameter param2 = new SqlParameter("@devicename", devicename);

    Task<int> dcaiResult =  Genkai_db.Database.ExecuteSqlCommandAsync("exec Refresh_DCAI @devicename", param2);

    Console.WriteLine("all set");

    Tlist.Add(mcResult);
    Tlist.Add(dcaiResult);

    await Task.WhenAll(Tlist.ToArray());
    int mc = await mcResult;
    int dc = await dcaiResult;

    Console.WriteLine("Finish Refresh" + mc + dc);
    return true;
}

虽然逻辑上可以同时发送,但第二个过程抛出了一个错误,因为第一个过程还没有完成。

谷歌翻译的错误信息:

'System.NotSupportedException' 类型的异常在 EntityFramework.dll 中发生,但未在用户代码中处理

其他信息:在上一个异步操作完成之前,在此上下文中启动了第二个操作。使用 "await" 确保在调用此上下文中的另一个方法之前已完成所有异步操作。不能保证任何成员实例是线程安全的。

那么问题在哪里?为什么我不能调用多个存储过程而不被 SQL Server 卡住呢?


1
Entity Framework 你不能这样做,但是你可以使用多个上下文来实现,不过我猜你也可以用普通的 ADO.NET 来完成。 - Sandip Bantawa
1个回答

7

更新

我认为EF目前不支持这一点,也许这是一个重复的问题,基于这个SO答案。很抱歉,这是无法完成的。

原文

问题在于你尝试了两次异步等待。当你将它们传递到await Task.WhenAll函数中时,它们在并行运行并被等待。然后之后,你又试图再次等待它们,而不是访问任务实例的.Result

请尝试下面的代码,让我知道它是否有效。

public async Task Refresh_Control(string devicename)
{
    Task<int> mcResult = 
        Genkai_db.Database.ExecuteSqlCommandAsync("exec Refresh_McAfee @devicename", 
            new SqlParameter("@devicename", devicename));
    Task<int> dcaiResult = 
        Genkai_db.Database.ExecuteSqlCommandAsync("exec Refresh_DCAI @devicename", 
            new SqlParameter("@devicename", devicename));

    await Task.WhenAll(mcResult, dcaiResult);

    int mc = mcResult.Result;
    int dc = dcaiResult.Result;

    Console.WriteLine("Finish Refresh :: mc=" + mc + ", dc=" + dc);
}

是的,错误不在等待上,而是在执行第二个过程的那一行。可能是EF或SQL服务器拒绝了执行。但我怀疑这个问题没有解决方法。 - Zwan
@David,你确认它的 EF 限制,这意味着我可以与比如 ADO 并行吗?我知道它也会出现 SQL 问题,但如果我只需要处理死锁,那么使用 ADO 或许更好。 - Zwan
@Zwan 是的,这个限制是由 EF 引起的。EF 抛出了异常,而直接使用 ADO.NET 可以让你同时执行两个操作。如果您认为我的回答正确,请将其标记为“已接受”。谢谢。 - David Pine
@DavidPine 我猜 await Task.WhenAll 是 OP 遇到问题的地方。 - Sandip Bantawa
1
@brykneval,这正是问题症状的体现——因为异步调用的调用会同时进行两个EF调用,EF会检测到这种情况并抛出异常。 - David Pine
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接