iT邦幫忙

1

C# 依賴注入到經new創建物件中

假設我有一個Service是在DI容器為Singleton,而該Service依賴DBContext,
之後該Service會定期創建一個新物件 - Connection
新的物件又會需要DBContext進行DB讀寫及LogService, 我如何將該兩樣傳入到Connection中?

Class BackgroundService 
{
    List<Connection> Connections;
    
    public BackgroundService(DBContext dbcontext);
    
    public void Timeout()
    {
        Connections.Add(new Connection("192.168.0.100", ?, ?));
    }
}

Class Connection
{
    public Connection(string ipAddress, DBContext dbcontext, ILogService logService);
}
ch_lute iT邦新手 5 級 ‧ 2020-06-23 14:13:13 檢舉
AddScope改用AddTransient,你應該是要這個?
https://ithelp.ithome.com.tw/articles/10193172
https://blog.johnwu.cc/article/ironman-day04-asp-net-core-dependency-injection.html
0
hem1102
iT邦新手 5 級 ‧ 2020-06-23 15:13:19
最佳解答

假設DBContext 是使用預設的Scoped,如果在BackgroundService(Singleton)注入Scoped時就會報錯,且不知道您要如何使用Connections,如果是我會有兩種做法:

  1. 如果是從Request Action執行階段才拿出DBContext,讓Connections去使用這個DBContext:
    public class BackgroundService
    {
        private readonly List<Connection> Connections;
        public BackgroundService()
        {
            Connections = new List<Connection>();
        }

        public void Timeout()
        {
            Connections.Add(new Connection("192.168.0.100"));
        }

        public void AllConnectionsDoSomething(DBContext dbcontext, ILogService logService)
        {
            foreach (var connection in Connections)
            {
                connection.DoSomething(dbcontext, logService);
            }
        }
    }

    public class Connection
    {
        private readonly string ipAddress;
        public Connection(string ipAddress)
        {
            this.ipAddress = ipAddress;
        }
        public void DoSomething(DBContext dbcontext, ILogService logService)
        {
            //....
        }
    }

    public class SampleController : Controller
    {
        private readonly DBContext dbcontext;
        private readonly ILogService logService;
        private readonly BackgroundService backgroundService;
        public SampleController(DBContext dbcontext, ILogService logService, BackgroundService backgroundService)
        {
            this.dbcontext = dbcontext;
            this.logService = logService;
            this.backgroundService = backgroundService;
        }

        public IActionResult SampleAction()
        {
            backgroundService.AllConnectionsDoSomething(dbcontext, logService);

            return Ok();
        }
    }
  1. 如果不是從Request去觸發的話,就自行創造一個"Scope",讓每一個Connection都是一個獨立的進程:
    public class BackgroundService
    {
        private readonly List<Connection> Connections;
        private readonly IServiceProvider serviceProvider;
        public BackgroundService(IServiceProvider serviceProvider)
        {
            Connections = new List<Connection>();
            this.serviceProvider = serviceProvider;
        }

        public void Timeout()
        {
            Connections.Add(new Connection("192.168.0.100", serviceProvider));
        }
    }

    public class Connection
    {
        private readonly string ipAddress;
        private readonly IServiceProvider _serviceProvider;
        public Connection(string ipAddress, IServiceProvider serviceProvider)
        {
            this.ipAddress = ipAddress;
            this._serviceProvider = serviceProvider;
        }
        public void DoSomething()
        {
            var scopeFactory = _serviceProvider.GetRequiredService<IServiceScopeFactory>();
            using (var scope = scopeFactory.CreateScope())
            {
                var serviceProvider = scope.ServiceProvider;
                var dbContext = serviceProvider.GetRequiredService<DBContext>();
                var logService = serviceProvider.GetRequiredService<ILogService>();

                //....
            }
        }
    }
0
ch_lute
iT邦新手 5 級 ‧ 2020-06-23 11:49:30

看不太懂,不是就傳進去嗎

Class BackgroundService 
{
    private readonly DBContext _dbcontext;
    List<Connection> Connections;
    
    public BackgroundService(DBContext dbcontext)
    {
        _dbcontext = dbcontext
    }
    
    public void Timeout()
    {
        Connections.Add(new Connection("192.168.0.100", _dbcontext, ?));
    }
}

fm119 iT邦新手 5 級 ‧ 2020-06-23 12:06:19 檢舉

DBContext預設是Scoped,代表每個Coonnection都會得到相同的DBContext。 能不能經由內置的DI容器使每個Connection都取得一個全新獨立的DBContext ?

w4560000 iT邦新手 3 級 ‧ 2020-06-23 12:38:46 檢舉
1
japhenchen
iT邦大師 1 級 ‧ 2020-06-23 12:46:21
Class Connection
{
    public DBContext mydbcontext {get;set;}
    public Connection(string ipAddress, DBContext dbcontext, ILogService logService)
    {
        mydbcontext = dbcontext ;
    }
}

//// todo 

public void readdb(){
    var data = thisconnection.mydbcontext.whattodo();
}
0

可以使用 IServiceProvider 取得 DI 物件。

public class BackgroundService
{
    private List<Connection> Connections;
    private readonly IServiceProvider _serviceProvider;

    public BackgroundService(
        IServiceProvider serviceProvider, 
        DBContext dbcontext)
    {
        _serviceProvider = serviceProvider;
    }

    public void Timeout()
    {
        Connections.Add(new Connection("192.168.0.100",
            _serviceProvider.GetRequiredService<DBContext>(),
            _serviceProvider.GetRequiredService<ILogService>()));
    }
}

我要發表回答

立即登入回答