![](/img/trans.png)
[英]Returning IEnumerable from Repository using ToList() vs using IList
[英]Returning IEnumerable straight from database or using ToListAsync before
當控制器直接從數據庫提供 IEnumerable 時,它們是如何工作的? 哪個代碼更正確和最優? 讓我們假設數據庫非常慢,並且還有其他操作正在進行。
這個例子非常簡單,因此執行時間可能沒有足夠的差異,但我正在努力學習最佳實踐。
public Task<Application[]> Find(Expression<Func<Application, bool>> predicate)
{
return DatabaseContext.Applications
.Where(predicate)
.ToArrayAsync();
}
...
public Task<Application[]> Find(...)
{
return ApplicationService.Find(...);
}
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);
}
public IEnumerable<Application> Find(Expression<Func<Application, bool>> predicate)
{
return DatabaseContext.Applications;
}
...
public IActionResult<IEnumerable<Application>> Find(...)
{
var applications = ApplicationService.Find(...);
return Ok(applications);
}
當控制器直接從數據庫提供 IEnumerable 時,它們是如何工作的?
(通過IEnumerable
我假設你的意思是直接從你的DbContext
返回一個未執行的IQueryable
)
他們不會,你也不應該——這是因為未執行的IQueryable
不代表加載的數據——並且當它被執行時,它只能從打開的數據庫連接加載數據——這需要一個活動且有效的DbContext
。
...因此,如果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()
和this.View()
不會觸發對視圖的評估或向客戶端發送對象響應 - 相反,它會導致控制器操作首先結束,然后將數據傳遞到ASP.NET 管道(即視圖)。 記住:視圖是在控制器動作完成后執行的。
如果您使用依賴注入在控制器中擁有一個准備好的DbContext
實例,那么結果將不太可預測: IQueryable
仍然可以在操作方法返回后進行評估,因為DbContext
不會在控制器被DbContext
之后被DbContext
,即通常在呈現視圖之后,但是您仍然不應該這樣做,因為您的IQueryable
仍然可以傳遞給某個進程,該進程比您的 Controller 類的生命周期更長,然后會導致失敗。 你也應該避免它,因為視圖被設計為快速和同步呈現 - 使用外部數據庫或 IO 調用會破壞該設計。
(無論如何,您都不應該使用 Entity Framework 實體對象作為根 ViewModel,但這是另一個討論)。
如果您總是在DbContext
上使用async
操作(例如ToListAsync()
、 ToDictionaryAsync
等ToListAsync()
,則可以避免這種習慣 - 因為它們分別返回Task<List<T>>
或TaskDictionary<TKey,TValue>>
- 這需要await
默認情況下,編譯器將阻止您在視圖或對象結果中執行操作(您可以在視圖中使用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 );
}
}
我會選擇選項 #2,因為您確切知道何時執行數據庫查詢。 由於您正在返回一個 Task 並正確使用 async 和 await 關鍵字,因此該框架將利用應用程序的吞吐量保持盡可能多的線程。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.