直接从数据库返回 IEnumerable 或之前使用 ToListAsync

当直接从数据库提供 IEnumerable 时,控制器如何工作?哪个代码更正确、更优化?假设数据库非常慢并且正在进行其他操作。


这个示例非常简单,因此执行时间可能没有足够的差异,但我正在尝试学习最佳实践。


#1

public Task<Application[]> Find(Expression<Func<Application, bool>> predicate)

{

    return DatabaseContext.Applications

        .Where(predicate)

        .ToArrayAsync();

}


...


public Task<Application[]> Find(...)

{

    return ApplicationService.Find(...);

}

#2

public Task<List<Application>> Find(Expression<Func<Application, bool>> predicate)

{

    return DatabaseContext.Applications

        .Where(predicate)

        .ToListAsync();

}


...


public async Task<IActionResult> Find(...)

{

    var applications = await ApplicationService.Find(...)

    return Ok(applications);

}

#3

public IEnumerable<Application> Find(Expression<Func<Application, bool>> predicate)

{

    return DatabaseContext.Applications;

}


...


public IActionResult<IEnumerable<Application>> Find(...)

{

    var applications = ApplicationService.Find(...);

    return Ok(applications);

}


肥皂起泡泡
浏览 148回答 2
2回答

呼啦一阵风

当直接从数据库提供 IEnumerable 时,控制器如何工作?(我假设你的意思是直接从你的IEnumerable返回一个未执行的)IQueryableDbContext他们不会,你也不应该- 这是因为未执行的IQueryable数据并不代表已加载的数据 - 当它执行时,它只能从打开的数据库连接加载数据 - 这需要一个活动且有效的DbContext....所以如果 被DbContext处置,则IQueryable无法执行。如果您DbContext在控制器内部创建操作并IQueryable在视图中渲染或在ObjectResponse(对于 Web API)中返回它,那么它总是会失败:public IActionResult GetPeople(){&nbsp; &nbsp; // WARNING: NEVER DO THIS!&nbsp; &nbsp; using( MyDbContext db = new MyDbContext( GetConnectionString() ) )&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return this.Ok( db.People.Where( p => p.Name == "John Smith" ) );&nbsp; &nbsp; &nbsp; &nbsp; // or:&nbsp; &nbsp; &nbsp; &nbsp; return this.View( model: db.People.Where( p => p.Name == "John Smith" ) );&nbsp; &nbsp; }}请记住,.Ok()andthis.View()不会触发视图的评估或向客户端发送对象响应 - 相反,它会导致控制器操作首先结束,然后将数据传递到 ASP.NET 管道中的下一步(即视图) 。请记住:视图在控制器操作完成后执行。如果您使用依赖注入在控制器中拥有一个现成的实例DbContext,那么结果就不太可预测:IQueryable在操作方法返回后仍然可以对 进行评估,因为DbContext直到控制器被处置之后才会被处置,通常是在视图已呈现,但是您仍然不应该这样做,因为您IQueryable仍然可能会传递到某个比您的 Controller 类的生命周期更长的进程,这会导致失败。您还应该避免它,因为视图被设计为快速同步渲染 - 外部数据库或 IO 调用会破坏该设计。(无论如何,您都不应该使用实体框架实体对象作为根 ViewModel,但这是另一个讨论)。如果您总是使用操作async(DbContext例如ToListAsync(),ToDictionaryAsync等 - 因为它们分别返回 aTask<List<T>>或TaskDictionary<TKey,TValue>>- 这需要一个await编译器默认情况下会阻止您在视图或对象结果中执行的操作,则可以避免这种习惯(您可以await在视图中使用,但这是不可取的,需要在某处设置一些设置)。简而言之,始终这样做:public async Task<IActionResult> GetPeople(){&nbsp; &nbsp; using( MyDbContext db = new MyDbContext( GetConnectionString() ) )&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; List<Person> list = await db.People&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Where( p => p.Name == "John Smith" )&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .ToListAsync();&nbsp; &nbsp; &nbsp; &nbsp; // WebAPI:&nbsp; &nbsp; &nbsp; &nbsp; return this.Ok( list ); // returning an evaluated list, loaded into memory. (Make sure Lazy Navigation Properties are disabled too)&nbsp; &nbsp; &nbsp; &nbsp; // MVC:&nbsp; &nbsp; &nbsp; &nbsp; PeopleListViewModel vm = new PeopleListViewModel(); // in MVC always use a custom class for root view-models so you're not accepting nor returning Entity Framework entity types directly&nbsp; &nbsp; &nbsp; &nbsp; vm.List = list;&nbsp; &nbsp; &nbsp; &nbsp; return this.View( vm );&nbsp; &nbsp; }}

拉风的咖菲猫

您正在返回一个由 mvc 框架执行的任务;当你等待一个任务时,你就开始(异步)运行它,然后获取结果并将其交给 mvc 框架;您将返回一个由 mvc 框架执行的枚举器。我会选择选项#2,因为您确切知道数据库查询何时执行。由于您要返回任务并正确使用 async 和 wait 关键字,因此框架将尽可能多地保持线程繁忙,从而利用应用程序的吞吐量。
打开App,查看更多内容
随时随地看视频慕课网APP