DI服務具有適當的緩存和dbContext用法

.net-core asp.net-core-mvc c# dependency-injection entity-framework-core

我真的想製作一個漂亮,乾淨和正確的代碼,所以我幾乎沒有什麼基本問題。一開始我有一個GetName服務。 _dbContext來自其他DI服務,如_cache

public Task<string> GetName(string title)
{
    var articleList = await _cache.GetOrCreate("CacheKey", entry =>
    {
        entry.SlidingExpiration = TimeSpan.FromSeconds(60 * 60);
        return _dbContext.Articles.ToListAsync;
    });

    var article = articleList.Where(c => c.Title == title).FirstOrDefault()

    if(article == null)
    {
        return "Non exist"
    }

    return article.Name();
}
  1. 官方文檔中,我們可以讀到EF上下文不是線程安全的,所以我應該在GetOrCreate返回後添加await
  2. 是否適合將其設置為Singleton服務並在操作和剃刀視圖中使用它?
  3. 官方文檔中,我們可以閱讀: 避免僅存在“數據持有者”對像以允許訪問 DI服務中的某些其他對象 。當我看到我的服務時,我擔心這條線。

熱門答案

EF上下文不是線程安全的事實與async \ await無關,所以你的第一個問題沒有多大意義。無論如何,一般最佳實踐是將多個EF上下文重用於多個邏輯操作。在多線程應用程序中違反此規則通常會立即導致災難。如果您一次使用來自單個線程的相同EF上下文 - 違規可能會導致某些意外行為,尤其是如果您長時間使用相同的上下文進行許多操作。

在ASP.NET中,每個請求都有專用線程,每個請求都不會持續很長時間,每個請求通常代表單個邏輯操作。因此,EF上下文通常使用作用域生存期進行註冊,這意味著每個請求都有單獨的上下文,這些上下文在請求結束時處理。對於大多數用途,此模式運行正常(但如果在單個http請求中執行具有許多數據庫請求的複雜邏輯,則可能仍會出現意外行為)。我個人仍然更喜歡為每個操作使用新的EF上下文實例(所以在你的例子中我會注入EF上下文工廠並在GetOrCreate體內創建新實例,然後立即處理)。但是,這不是一個非常流行的觀點。

因此,每個請求都有單獨的上下文(如果它使用作用域生存期註冊),因此您不應該(通常,如果您不自己生成後台線程),那麼就不必考慮對該實例的多線程訪問。

您的方法仍然不正確,因為您現在保存在緩存中的不是List<Article> 。您存儲Task<List<Acticle>> 。使用內存緩存可以正常工作,因為存儲在內存緩存中不涉及任何序列化。但是一旦你改變你的緩存提供者 - 事情就會破裂,因為Task顯然不是可序列化的。而是使用GetOrCreateAsync (注意return後仍然沒有理由添加await ):

var articleList = await _cache.GetOrCreateAsync("CacheKey", entry =>
{
    entry.SlidingExpiration = TimeSpan.FromSeconds(60 * 60);
    return _dbContext.Articles.ToListAsync();
});

請注意,如果項目在緩存中已經存在-將不會有異步工作完成,整個操作將完全同步執行(不要讓await欺騙你-如果你await的東西它並不一定意味著會有一些後台線程或涉及異步操作)。

將EF實體直接存儲在緩存中通常不好。最好將它們轉換為DTO實體並存儲它們。但是,如果存儲EF實體 - 至少不要忘記為上下文禁用延遲加載和代理創建。

將此類服務註冊為singleton是不合適的,因為它依賴於具有作用域生存期的EF上下文(默認情況下)。使用與EF上下文相同的生命週期註冊它。

至於避免“數據持有者”對象 - 我不知道它在這裡是如何相關的。您的服務具有邏輯(從緩存數據庫獲取數據) - 它不存在允許訪問其他一些對象。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow