【问题标题】:How to call class with dependency injection (DI)如何使用依赖注入 (DI) 调用类
【发布时间】:2020-07-08 09:34:23
【问题描述】:

说明

我想用依赖注入创建一个类的对象。如果我手动设置参数,我会得到异常Cannot access a disposed of the object.

此应用程序是带有 Dotnet core 3.1 的 Blazor wasm。我创建了一个应该连接到查询控制台的中间件。所以我有一个包含所有查询客户端的静态列表。如果缺少客户端,则会创建它。

在中间件中调用异步:

public async Task InvokeAsync(HttpContext context,
    IConfiguration configuration,
    IInstanceControlRepository instanceControlRepository,
    IServiceProvider serviceProvider)
{
    _configuration = configuration;
    _instanceControlRepository = instanceControlRepository;

    long timestamp = new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds();
    var instances = _instanceControlRepository.GetAllInstances();

    if (_time + 3 <= timestamp)
    {
        _time = timestamp;

        // Remove
        foreach(var client in TeamspeakInstanceQueryClients.ToList())
        {
            var cl = instances.ToList().Find(el => el.Id == client.Instance.Id);
            
            if(cl == null)
            {
                client.Dispose();
                TeamspeakInstanceQueryClients.RemoveAll(el => el.Instance.Equals(client.Instance));
            }
        }   

        // Create & Update
        foreach (var instance in instances)
        {
            var queryClient = TeamspeakInstanceQueryClients.Find(el => el.Instance.Id == instance.Id);

            if(queryClient == null)
            {
                //var test = ActivatorUtilities.CreateInstance<ApplicationDbContext>(serviceProvider);
                //var dbContext = serviceProvider.GetService<ApplicationDbContext>();
                //queryClient = new TeamspeakInstanceQueryClient(new InstancesControlRepository(ActivatorUtilities.CreateInstance<ApplicationDbContext>(serviceProvider)));
                queryClient = new TeamspeakInstanceQueryClient(serviceProvider);
                _ = queryClient.Connect(instance);
                TeamspeakInstanceQueryClients.Add(queryClient);
            }
            else
            {
                _ = queryClient.CheckInstanceData(instance);
            }
        }
    }

    await _next(context);
}

TeamspeakInstanceQueryClient.cs

public partial class TeamspeakInstanceQueryClient : ITeamspeakInstanceQueryClient
{
    private IInstanceControlRepository _instanceControlRepository;

    private const short MAX_RETRYS = 3;
    private const short TIME_TO_RETRY = 10;

    private EventHandler OnConnected;

    public Instance Instance { get; internal set; }
    public TeamSpeakClient Client { get; internal set; }
    public bool IsSelected { get; internal set; }
    private short _connectionTrys = 0;

    public TeamspeakInstanceQueryClient(IServiceProvider serviceProvider)
    {
        _instanceControlRepository = new InstancesControlRepository(ActivatorUtilities.CreateInstance<ApplicationDbContext>(serviceProvider));
        Init();
    }
}

InstancesControlRepository.cs

public class InstancesControlRepository : IInstanceControlRepository
{
    private readonly ApplicationDbContext _applicationDbContext;

    public InstancesControlRepository(ApplicationDbContext applicationDbContext)
    {
        _applicationDbContext = applicationDbContext;
    }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(option =>
        option.UseMySql(
            Configuration.GetConnectionString("DefaultConnection"),
            mySqlOptions => mySqlOptions.ServerVersion(new System.Version(10, 4, 13), ServerType.MariaDb)
        )
    );
    services.AddScoped<IInstanceControlRepository, InstancesControlRepository>();
    services.AddScoped<IServerQueryRepository, ServerQueryRepository>();

我试过的

  1. 我尝试使用服务提供者创建类,但遇到了同样的错误
  2. 我尝试在创建的类中使用服务提供者创建缺少的参数。但我需要注入服务提供者,这也会创建异常Cannot access a disposed of the object. Object name: 'IServiceProvider'.
  3. 我已尝试将服务提供者设为静态,因此它无法被处置,但已被处置。

【问题讨论】:

  • 为什么你的 TeamspeakInstanceQueryClient 类会创建一个 InstancesControlRepository 的实例,而不是简单地在 DI 容器中注册 InstancesControlRepository 并让其处理初始化类型?
  • 你的意思是在ClientFactory中?

标签: c# dependency-injection blazor webassembly blazor-webassembly


【解决方案1】:

似乎IServiceProvider 的实例是a scoped one 并且在范围结束时被释放(我假设在请求结束时)。您可以尝试为您的 TeamspeakInstanceQueryClient 定义单例工厂并使用它:

class ClientFactory
{
    private IServiceProvider _sp { get; set; }        
    private IServiceScope _scope  { get; set; }
    public  MyClass(IServiceProvider sp)
    {
        _sp = sp;
        _scope = sp.CreateScope();
    }
    public TeamspeakInstanceQueryClient Create() => new TeamspeakInstanceQueryClient(_scope.ServiceProvider);
}

// register it as singleton 
services.AddSingleton<ClientFactory>();

并在InvokeAsync中使用它:

var factory = serviceProvider.GetRequiredService<ClientFactory>();
queryClient = factory.Create();

附:此代码可以大大改进,仅用于演示目的。

【讨论】:

  • 看起来它不起作用,因为存储库有一个 EF Dbcontext。我收到了这个错误 => System.InvalidOperationException: Cannot resolve scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions`1[PhoenixBot.Server.ApplicationDbContext]' from root provider。
  • 您可以创建子作用域并将其存储在工厂中并使用它。请参阅更新后的帖子。可能需要根据TeamspeakInstanceQueryClient创建作用域,视实际情况而定。
  • 看起来有效。但仅供我参考。这个工厂创建了一个新的“生命周期容器”来创建作用域 DI?
  • @LukasGund 有范围的概念。单例具有使用services.BuildServiceProvider() 构建的根目录,然后它可以创建范围 - 通常的场景之一是每个对 Web 服务的请求,每个范围将共享 Scoped 服务(因此在范围内你将始终得到相同的范围服务)。当作用域结束时,它会处理所有实例化的作用域和瞬态服务实现IDIsposable(在一般情况下,其他一些容器也可以有不同的行为)。
猜你喜欢
  • 2016-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-16
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多