【问题标题】:Read content from files once ... Static or Singleton?一次从文件中读取内容...静态还是单例?
【发布时间】:2019-11-05 08:07:06
【问题描述】:

使用 ASP.NET Core 3.0 我有一个 PostService,它可以读取一些 Markdown 文件的内容:

public class PostService : IPostService {

  private String _basePath;

  public PostService(IWebHostEnvironment _webHostEnvironment) {
    _basePath = _webHostEnvironment.WebRootPath;      
  }

  public async Task<IList<Post>> GetAllAsync() {

    IList<Post> _posts = new List<Post>();

    String[] files = Directory.GetFiles($"{_basePath}/posts", "*", SearchOption.AllDirectories);

    foreach (String file in files) {

      String content = await File.ReadAllTextAsync(file);

      // Process content and create 'post' from content 

      _posts.Add(post);

    }

    return _posts;

  }

}

我在控制器上按如下方式使用它:

public class PostController : ControllerBase {   

    public PostController(IPostService postService) {
      _postService = postService;      
    }

    [HttpGet("posts/{id}")]
    public async Task<IActionResult> Get(Int32 id) {

      Post post = _postService.GetAllAsync().FirstOrDefault(x => x.Id == id);

      // Remaining code
    } 
}

我不想在每个请求中加载文件,因为它们不会改变。

我应该将 PostService 添加为 Singleton 吗?

services.AddSingleton<IPostService, PostService>();   

我应该将帖子存储在 PostService 中的静态变量中吗?

避免一直加载和读取文件的最佳方法是什么?

更新

在 PostService 中使用静态字段类似于:

public class PostService : IPostService {

  private static List<Post>() _posts;

  private String _basePath;

  public PostService(IWebHostEnvironment _webHostEnvironment) {
    _basePath = _webHostEnvironment.WebRootPath;      
  }

  public async Task<IList<Post>> GetAllAsync() {

    // Check if Posts were already loaded.
    if (_posts != null)
      return _posts; // If yes then return _posts and do not load files

    String[] files = Directory.GetFiles($"{_basePath}/posts", "*", SearchOption.AllDirectories);

    foreach (String file in files) {

      String content = await File.ReadAllTextAsync(file);

      // Process content and create 'post' from content 

      _posts.Add(post);

    }

    return _posts;

  }

}

这是要走的路吗?我应该在构造函数中加载帖子吗?

【问题讨论】:

  • 将此作为服务的目的是什么?测试?
  • 是的,用于测试......并且还有一个类负责加载和解析将在一些控制器中使用的文件。
  • 是否有可能同时调用GetAllAsync()?如果是这样,静态变量解决方案可能会遇到变量被多次加载的问题,如果/当多个请求进入方法而变量仍然为空时
  • 你考虑过把它放到cache吗?
  • @devNull 是的,处于可能发生的 Web 应用程序中。

标签: c# asp.net-core asp.net-core-3.0


【解决方案1】:

如果您将课程设置为单例,您将谴责它永远如此 - 单例。该课程(我假设)将做的不仅仅是存储帖子。您可能会发现您需要它不是单例。

我会将列表存储为静态变量。

还有其他方法可以将缓存项保存在内存中,并为此目的进行了优化(例如 System.Runtime.Caching.MemoryCache)。

【讨论】:

  • 我刚刚更新了我的问题。我应该在静态构造函数中加载帖子吗?还是我在更新中建议的?
  • 当类开始做的不仅仅是缓存帖子时,可以添加一个新服务。我不买谴责论点:)
  • 我想在课堂上这样做的原因之一是,如果将来有人错误地将服务设置为瞬态,那么这不会有问题,因为课堂会“缓存" 帖子 ...
  • @MiguelMoura 回应问题和一些cmets的变化。如果您必须注入一些东西(例如 IWebHostEnvironment),那么显然很难从静态构造函数中获取信息。您是否考虑过从类中单独获取这些信息(例如在应用程序启动时),然后从类中访问它?
  • @AngryHacker 我更喜欢注入它,因为我需要编写一些测试。
【解决方案2】:

我认为这两个选项都可以。


在 PostService 中使用静态字段

静态构造函数将是开箱即用的线程安全的。需要使延迟加载的成员成为线程安全的。

【讨论】:

  • 我在使用静态构造函数时遇到的一个问题是我需要在服务中注入一个实例来获取 BasePath。
  • 这听起来像是静态构造函数的否定。
猜你喜欢
  • 1970-01-01
  • 2015-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
  • 2013-01-02
  • 1970-01-01
相关资源
最近更新 更多