簡體   English   中英

直接從數據庫返回 IEnumerable 或之前使用 ToListAsync

[英]Returning IEnumerable straight from database or using ToListAsync before

當控制器直接從數據庫提供 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);
}

當控制器直接從數據庫提供 IEnumerable 時,它​​們是如何工作的?

(通過IEnumerable我假設你的意思是直接從你的DbContext返回一個未執行的IQueryable

他們不會,你也不應該——這是因為未執行的IQueryable不代表加載的數據——並且當它被執行時,它只能從打開的數據庫連接加載數據——這需要一個活動且有效的DbContext

...因此,如果DbContextDbContext ,則無法執行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()ToDictionaryAsyncToListAsync() ,則可以避免這種習慣 - 因為它們分別返回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 );
    }
}
  1. 您正在返回一個要由 mvc 框架執行的任務;
  2. 一旦等待任務,您就在運行(異步)任務,然后獲取結果並將其交給 mvc 框架;
  3. 您正在返回一個由 mvc 框架執行的枚舉器。

我會選擇選項 #2,因為您確切知道何時執行數據庫查詢。 由於您正在返回一個 Task 並正確使用 async 和 await 關鍵字,因此該框架將利用應用程序的吞吐量保持盡可能多的線程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM