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