0

I have the following code:

public async Task ExecuteJobs(CancellationToken ct)
{
    var newJobs = (await _jobRepository.GetAsync(ct, x => x.State == State.Created && x.RunDate <= DateTimeOffset.Now)).ToList();
    
    foreach (var job in newJobs)
    {
        try
        {
            UpdateJobState(job, State.Scheduled);
            switch (job.Type)
            {
                case JobType.AnnualReportConfiguration:
                    UpdateJobState(job, State.Running);
                    await _reportConfigurationDataService.GetAndSendData(ct);
                    break;
                case JobType.Assets:
                    UpdateJobState(job, State.Running);
                    await _assetsDataService.GetAndSendData(ct);
                    break;
                case JobType.BrfEconomy:
                    UpdateJobState(job, State.Running);
                    await _brfEconomyDataService.GetAndSendData(ct);
                    break;
                case JobType.Customer:
                    UpdateJobState(job, State.Running);
                    await _customerDataService.GetAndSendData(ct);
                    break;
                case JobType.ElectedRepresentative:
                    UpdateJobState(job, State.Running);
                    await _representativeDataService.GetAndSendData(ct);
                    break;
                case JobType.Person:
                    UpdateJobState(job, State.Running);
                    await _personDataService.GetAndSendData(ct);
                    break;
                case JobType.RealEstate:
                    UpdateJobState(job, State.Running);
                    await _realEstateDataService.GetAndSendData(ct);
                    break;
                case JobType.TextTemplate:
                    UpdateJobState(job, State.Running);
                    await _textTemplateDataService.GetAndSendData(ct);
                    break;
            }

            UpdateJobState(job, State.Finished);
        }
        catch(Exception ex)
        {
            UpdateJobState(job, State.Error);
        }
    }
}

    private void UpdateJobState(Job job, State jobState)
    {
        job.State = jobState;
        _jobRepository.Update(job);
    }

I wonder how I can run the jobs independently of each other, instead of waiting for each other? How can I do this in a good way, consider that entity framework is not thread-safe?

I have tried something like this:

case JobType.Assets:
 UpdateJobState(job, State.Running);
 var task = Task.Run(() => _assetsDataService.GetAndSendData(ct));
 task.ContinueWith((task) =>
 {
    UpdateJob(job.Id, State.Finished, ct);
 });
 break;

    private async Task UpdateJob(Guid id, State jobState, CancellationToken ct)
    {
        var job = (await _jobRepository.GetAsync(ct, x => x.Id == id)).FirstOrDefault();
        job.State = jobState;
        _jobRepository.Update(job);
    }

But that don't work because I get an exception saying that the DbContext is used in another thread.

Can you guys give me some feedback of how I should do it?

EDIT:

I solved the entity framework issue by implementing a factory, and It worked.

However,

I get the following error when my Task is completed:

Cannot access a disposed object.\r\nObject name: 'IServiceProvider'.

This is when this code is executed:

estateTask.ContinueWith((estateTask) => UpdateJob(job.Id, State.Finished, ct));

    private async Task UpdateJob(Guid Id, State jobState, CancellationToken ct)
    {
        try
        {
            var job = (await _jobRepository.Get(x => x.Id == Id, ct)).FirstOrDefault(); // error here
            job.State = jobState;
            _jobRepository.Update(job);
        }
        catch(Exception ex)
        {
            throw;
        }
    }

How can I solve this?

  • 2
    EF context is not thread safe, so you need to instantiate a new one for each job. – Guru Stron Jul 22 '20 at 10:06
  • @GuruStron: And how should I do that? In my services? Im using a generic repository that is using the context.. – JuggernautDev Jul 22 '20 at 10:08
  • @JuggernautDev A generic repository decouples from the persistence layer, so it's going to be difficult to optimise that. – Johnathan Barclay Jul 22 '20 at 10:12
  • @JuggernautDev there is not enough code in this sample to say how, my usual approach - to create nested scope via DI library I use and resolve a needed service(repository/context). – Guru Stron Jul 22 '20 at 10:19
  • @JohnathanBarclay If a switch to a "specific" repository, like JobRepository, that implements a ContextFactory like this: `private readonly IJobContextFactory_contextFactory; public JobRepository(IJobContextFactory contextFactory) { _contextFactory = contextFactory; }` would that work? – JuggernautDev Jul 22 '20 at 10:20
  • @GuruStron: Can you show a example? – JuggernautDev Jul 22 '20 at 10:20
  • @JuggernautDev Something like [this](https://stackoverflow.com/a/61602061/2501279). – Guru Stron Jul 22 '20 at 10:24
  • @GuruStron: Do u think it would work with a ContextFactory? I create a JobRepository that uses the ContextFactory? – JuggernautDev Jul 22 '20 at 10:26
  • @JuggernautDev It should. But I would say it should be done inside `IJobContextFactory` from the name of it, but without actual code and understanding what `IJobContextFactory` actually is it is hard to say. – Guru Stron Jul 22 '20 at 10:28
  • @GuruStron: Check my updated question. – JuggernautDev Jul 22 '20 at 11:16
  • @JuggernautDev not enough info, but it seems that nested scope is disposed too early. – Guru Stron Jul 22 '20 at 11:37

0 Answers0