简体   繁体   中英

EF Core: Create an object by passing an injected DbContext object as parameter

I have created a.Net Core MVC project and understand that how the dependency injection works for our MVC controller as shown below, but same like I wanted to create an object for my own class by calling the same injected interface/class as a parameter.

public class ShiftsController : BaseController
{
  ShardingDbContext _dbContext;

   public ShiftsController(ShardingDbContext ShardingDbContext) : base(ShardingDbContext)
  {
   _dbContext = ShardingDbContext;
    ViewBag.Menu = BuildMenu();
  }

I have injected the DbContext into my Startup.cs file as below,

 //Entity Framework Core
 services.AddDbContext<ShardingDbContext>(options => options.UseSqlServer(ConnectionString), 
 ServiceLifetime.Transient);

The ShiftsController is a C#-MVC controller and the DbContext is working perfectly when I run my app and go to Shift's page in my application, but when I try like below-given code, it's not working and gives an error. So I don't know how to pass the registered class's object while creating an object by using "new" keyword.

public class JobScheduler
{
  ShardingDbContext _dbContext;


  public JobScheduler(ShardingDbContext ShardingDbContext)
  {
      _dbContext = ShardingDbContext;

  }...

This is my own class and tried to create an object for the class JobScheduler as shown below.

  JobScheduler  jobs = new JobScheduler();

So now I don't know how to pass the EF core's DbContext's object to the constructor JobScheduler, the DI works fine for the controller but not for a normal class. Can anyone help with this and I am eagerly waiting to understand this logic as well?.

You are right: Your DI works fine but your ShardingDbContext is not passed into your JobScheduler because you are not using DI to instanciate JobScheduler . Whenever you are explicitly creating an object instance using the new keyowrd you are not using DI.

You have two options:

  1. Wherever you are calling new JobScheduler() let DI inject you a ShardingDbContext through the constructor and pass it to JobScheduler like so new JobScheduler(shardingDbContext)
  2. Register JobScheduler to the dependency injection as well and let DI build up the whole chain so you don't need to call new JobScheduler() but rather get a JobScheduler injected directly wherever you need it

Edit

As requested here is the example for a timed job using a short lived DB context:

public class TimedBackgroundService : IHostedService, IDisposable
{
    private readonly Timer timer;
    private readonly IServiceProvider serviceProvider;

    public TimedBackgroundService(IServiceProvider serviceProvider)
    {
        timer = new Timer(async state => await ExecuteAsync());
        this.serviceProvider = serviceProvider;
    }

    public Task StartAsync(CancellationToken stoppingToken)
    {
        timer.Change(0, TimeSpan.FromMinutes(30));

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken stoppingToken)
    {
        timer.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose() => timer.Dispose();

    private async Task ExecuteAsync()
    {
        try
        {
            using var scope = serviceProvider.CreateScope();
            var job = scope.ServiceProvider.GetRequiredService<MyJob>();

            await job.Execute();
        }
        catch (Exception exception)
        {
            // log error here
            return;
        }
    }
}

The MyJob class wil look something like this:

public class MyJob
{
    private readonly ShardingDbContext dbContext;

    public MyJob(ShardingDbContext dbContext)
    {
        this.dbContext = dbContext;
    }

    public Task Execute()
    {
        // Your logic goes here
    }      
}

Then you register your classes in the startup like so:

services
    .AddHostedService<TimedBackgroundService>()
    .AddScoped<MyJob>();

Now you have a job which runs every 30 minutes and uses a short lived db context.

Register your JobScheculer like this:

services.AddSingleton<JobScheduler>();

then use your dbContext like this:

public class JobScheduler
{
   private readonly IServiceProvider provider;

   public JobScheduler(IServiceProvider provider)
   {
       

   }...


   public (or private etc) DoYourJob()
   {
        using (var scope = provider.CreateScope()) 
        {
            var dbContext = scope.GetService<ShardingDbContext>();
            //use it here
        }
   }

At the end of the ConfigureServices method of the Startup.cs class, and I did not change anything in the JobSchedulerclass and passing the DbContext object from the service provider as shown below, thanks to everyone who tried to help with this question.

 public void ConfigureServices(IServiceCollection services)
 { 
   ...
   ...

   JobScheduler job = new 
   JobScheduler(services.BuildServiceProvider().CreateScope().ServiceProvider
   .GetService<ShardingDbContext>());
   
   job.Start();
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM