从实体框架加载列表而不让控制器等待

本文关键字:控制器 等待 列表 实体 框架 加载 | 更新日期: 2023-09-27 18:18:32

我想在我的Web应用程序的开始加载一组列表,以便以后使用它们。这些列表是静态的,使用实体框架作为ORM从数据库中读取。我们的想法是在开始时加载列表(在登录后的主页上),并在整个de App中使用它们。但我不希望主页等待列表完成加载。我已经尝试了几个替代方案,但没有一个工作(或同步或有错误)。

1。第一次尝试:调用ToListAsync()并等待ToListAsync(Throw Exception from EF)

列表有两个版本,一个是异步的,另一个是同步的:

private static Task<List<EMPRESA>> ListaEmpresasAsync;
public static List<EMPRESA> ListaEmpresas

我已经定义了一个函数,从EF

上的存储库生成列表。
public async static Task<List<T>> GenerateAsyncListFromRepository(IGenericRepositoryBlockable<T> repository)
{
    IQueryable<T> queryAsync = repository.GetAllActive();
    return await queryAsync.ToListAsync();
}

和其他函数检查异步调用的结果:

public static List<T> ForceToLoadAsyncList(Task<List<T>> task)
{
    task.Wait();
    return task.Result;
}

异步加载列表:

ListaEmpresasAsync = TaskManager<EMPRESA>.GenerateAsynListFromRepository((IEmpresaRepositorio)DIResolver.GetService(typeof(IEmpresaRepositorio)));

当需要List时,我强制加载:

public static List<EMPRESA> ListaEmpresas
{
    get
    {
        return TaskManager<EMPRESA>.ForceToLoadAsyncList(ListaEmpresasAsync);               
    }
}

这个初始方法抛出以下错误:

在前一个异步操作完成之前,在此上下文中开始了第二个操作

2。第二:使用ToList()并等待任务结束。run()(空枚举的问题)

然后我测试使用ToList()代替ToListAsync:
return await Task.Run(() => { return queryAsync.ToList(); });

我也不工作。

3。第三:使用ToList()并等待GenerationList, Force只返回List(行为类似于同步方法)。控制器正在等待所有列表加载)

按照这种方法,我将函数的签名更改为返回list:

return queryAsync.ToList();

并等待加载进程

ListaEmpresasAsync = await TaskManager<EMPRESA>.GenerateAsynListFromRepository((IEmpresaRepositorio)DIResolver.GetService(typeof(IEmpresaRepositorio)));

但是这与同步过程的工作原理相似,这意味着在主页上加载时间非常长。

我知道在EF上,每个上下文只允许一个异步调用。我只是想把所有这些加载进程放在后台,即使他们运行同步,当列表需要检查任务的结果。

任何想法?

只是为了澄清,Yuval Itzchakov提出的解决方案应该在DbContext不被所有加载列表调用共享时工作,但在我的场景中,它抛出了与实体框架上多个异步调用相关的错误。

从实体框架加载列表而不让控制器等待

您的代码有几处错误。首先,您需要知道,即使使用async-await进行异步调用,在等待的操作返回之前,请求也不会完成。这一点很重要,因为如果你需要的是Fire and Forget样式的操作,这是行不通的。

如果您想要异步执行(而不是触发并忘记),请执行以下操作:

public static List<EMPRESA> ListaEmpresas
public static Task<List<T>> GenerateAsyncListFromRepository(IGenericRepositoryBlockable<T> repository)
{
    return repository.GetAllActive().ToListAsync();
}

然后像这样调用它:

ListaEmpresas = await TaskManager<EMPRESA>.GenerateAsyncListFromRepository((IEmpresaRepositorio)DIResolver.GetService(typeof(IEmpresaRepositorio)));

创建两个列表并使用Task.Wait是无用的,你没有强迫任何其他的线程等待async方法返回,这意味着它是同步运行的。

如果您想要的是,请注意以下内容:

在ASP中使用Task.Run. NET是危险的,因为它不向asp.net注册排队的工作。. NET线程池,并且暴露于IIS回收,这可能导致您的后台工作意外终止。相反,如果你使用的是。net 4.5.2,你可以使用HostingEnvironment.QueueBackgroundWorkItem来为你注册队列任务。如果没有,看看Stephan Clearys的BackgroundTaskManager

您可以使用EF执行此并行任务,但是在并行线程中使用1 DBContext的意图将以失败告终。DBContext的实例成员不是线程安全的!DBContext信息

你可以使用一个简单的模式来跨越线程

    线程n + 1
  • 线程N + 2

等待结果。
每个线程必须使用自己的上下文。对于给定的搜索字符串,我使用此模式一次搜索许多对象类型。但是要有一个等待的整体结果是设计的一部分。我不确定这是否符合你的要求。

并行任务演示

如果您有非易失性数据集,您可以让IIS缓存结果。我在非易失性内容的控制器方法上使用这种方法。参见OutputCache注释。

       [OutputCache(Duration = int.MaxValue, VaryByParam = "id", Location = OutputCacheLocation.ServerAndClient)]