【问题标题】:Cannot consume scoped service from singleton无法使用单例的范围服务
【发布时间】:2019-08-20 20:11:42
【问题描述】:

我正在尝试在托管服务中使用 DBContext,但收到此错误。 我尝试关注this 接受的答案,但不知何故它不起作用,我不确定我做错了什么。

我是 .net 新手,请引导我走向正确的方向。

未处理的异常:System.InvalidOperationException:无法使用 来自单例的范围服务“StatusApp.Context.DBContext” 'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor'。

public class TokenService : IHostedService
{
    public IConfiguration _Configuration { get; }
    protected IMemoryCache _cache;
    private Timer _timer;
    public IHttpClientFactory _clientFactory;
    public DBContext _DBcontext;

    private readonly IServiceScopeFactory _scopeFactory;

    public TokenService(IConfiguration configuration, IMemoryCache memoryCache, IHttpClientFactory clientFactory, DBContext DBcontext, IServiceScopeFactory scopeFactory)
    {
        _Configuration = configuration;
        _cache = memoryCache;
        _clientFactory = clientFactory;
        _scopeFactory = scopeFactory;
        _DBcontext = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<DBcontext>();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _timer = new Timer(getOrg, null, 0, 1000);
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        //Timer does not have a stop. 
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    public async Task getOrg()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
        var response = await _client_NP.SendAsync(request);
        var json = await response.Content.ReadAsStringAsync();
        OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);

        foreach (var item in model.resources)
        {
            var g = Guid.Parse(item.guid);
            var x = _DBcontext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
            if (x == null)
            {
                _DBcontext.Organizations.Add(new Organizations
                {
                    OrgGuid = g,
                    Name = item.name,
                    CreatedAt = item.created_at,
                    UpdatedAt = item.updated_at,
                    Timestamp = DateTime.Now,
                    Foundation = 3
                });
            }
            else if (x.UpdatedAt != item.updated_at)
            {
                x.CreatedAt = item.created_at;
                x.UpdatedAt = item.updated_at;
                x.Timestamp = DateTime.Now;
            }
        }

        await _DBcontext.SaveChangesAsync();
    }
}

【问题讨论】:

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


    【解决方案1】:

    您快到了,但您将DBContext 作为依赖项留在了TokenService 的构造函数中。删除它,您将不再收到错误。

    公共令牌服务( IConfiguration 配置, IMemoryCache 内存缓存, IHttpClientFactory 客户端工厂, 数据库上下文数据库上下文, IServiceScopeFactory 范围工厂)

    但是,您并没有完全遵循在单例服务中处理 DbContext 的建议。不要在构造函数中创建DBContext 的单个实例并将其存储为字段,而是在需要时创建一个范围和相应的DBContext。在您的情况下,这是在 getOrg 方法中。

    按照以下步骤实现:

    1. TokenService 类中删除_DBcontext 字段:

      公共DBContext _DBcontext;

    2. TokenService 构造函数中移除关联的赋值:

      _DBcontext = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService();

    3. getOrg 中,创建一个作用域,解析DBContext 的一个实例,最后释放作用域:

      public async Task getOrg()
      {
          var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
          var response = await _client_NP.SendAsync(request);
          var json = await response.Content.ReadAsStringAsync();
          OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);
      
          using (var scope = _scopeFactory.CreateScope())
          {
              var dbContext = scope.ServiceProvider.GetRequiredService<DBcontext>();
      
              foreach (var item in model.resources)
              {
                  var g = Guid.Parse(item.guid);
                  var x = dbContext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
                  if (x == null)
                  {
                      dbContext.Organizations.Add(new Organizations
                      {
                          OrgGuid = g,
                          Name = item.name,
                          CreatedAt = item.created_at,
                          UpdatedAt = item.updated_at,
                          Timestamp = DateTime.Now,
                          Foundation = 3
                      });
                  }
                  else if (x.UpdatedAt != item.updated_at)
                  {
                      x.CreatedAt = item.created_at;
                      x.UpdatedAt = item.updated_at;
                      x.Timestamp = DateTime.Now;
                  }
              }
      
              await dbContext.SaveChangesAsync();
          }
      }
      

    上面的代码没有使用 _DBContext 字段,而是创建了一个局部的、范围适当的变量 dbContext 并改为使用它。

    【讨论】:

    • 我在StartAsync 函数的_timer 行中遇到错误Argument 1: cannot convert from 'method group' to 'TimerCallback',你知道有什么问题吗?
    • 那是因为回调的签名应该是void,但你的签名是Task。 David Fowler 对处理这个问题有一些很棒的建议here
    猜你喜欢
    • 2019-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    • 2018-01-30
    • 2021-01-11
    相关资源
    最近更新 更多