Base solution for your next web application
Open Closed

HangFire and UnitOfWork #1062


User avatar
0
nicolas33 created

Hi all...

I have some issues using Hangfire when I need to access Database. I've read here that I maybe shouldn't use HangFire to call Application Service but I don't see how I could do what I want to do another way.

Actually I have in my Startup.cs from my Web project :

[...]

app.UseHangfireDashboard();
app.UseHangfireServer();

RecurrentJobs recurrentJobs = new RecurrentJobs(IocManager.Instance);
RecurringJob.AddOrUpdate(() => recurrentJobs.LaunchJobsRecurrentFiveMinutes(), "0,5,10,15,20,25,30,35,40,45,50,55 * * * *");

[...]

In the RecurrentJobs.cs file, I have this call, which try to call a method in the ApplicationService ("AjouterMessageSysteme") :

[...]

        [UnitOfWork]
        public void LaunchJobsRecurrentFiveMinutes()
        {
            [...]
                using (var messageService = _iocResolver.ResolveAsDisposable<MessageAppService>())
                {
                    var source = LocalizationHelper.Manager.GetSource("MyProject");

                    foreach (var concoursId in retourMajQualif.ListeIdConcoursQualifMaj)
                    {
                        messageService.Object.AjouterMessageSysteme(
                        new AjouterMessageInput
                        {
                            ConcoursId = concoursId,
                            Contenu = source.GetString("finQualifications")
                        });
                    }
                }

            [...]
            }
        }

[...]

In the MessageAppService.cs I have this code :

[...]

        public AjouterMessageOutput AjouterMessageSysteme(AjouterMessageInput input)
        {
            var concours = _concoursRepository.GetAllWithParticipants().Where(c => c.Id == input.ConcoursId).FirstOrDefault();

            foreach (var part in concours.ListeParticipants)
            {
                _messageRepository.Insert(new Message
                {
                    ConcoursId = input.ConcoursId,
                    Contenu = input.Contenu,
                    DestinataireId = part.UserId,
                    EnvoyeurId = 0
                });
            }

            return new AjouterMessageOutput();
        }

[...]

The issue is when I call the method " _concoursRepository.GetAllWithParticipants()", which call a GetAll() with some includes. The object "concours " is always disposed before it can be used. I guess it's because the whole process isn't a "Unit Of Work", but I don't know how I can declare it beside the annotation on method in the class "RecurrentJobs". I succesfully could call a procedure from database, but I can't get data. Of course, if the method of the application service is called from a controller, it works perfectly.

Is there a way I can do this, or ABP/HangFire isn't designed to work like this ?

I apologize for my approximative english, thanx in advance for any help.


3 Answer(s)
  • User Avatar
    0
    nicolas33 created

    I found a way, in the RecurrentJobs.cs file, using the "Object.UnitOfWorkManager" of my appService.

    [...]
    
            public void LaunchJobsRecurrentFiveMinutes()
            {
                [...]
                    using (var messageService = _iocResolver.ResolveAsDisposable<MessageAppService>())
                    {
                        var source = LocalizationHelper.Manager.GetSource("MyProject");
    
                        using (var unitOfWork = messageService.Object.UnitOfWorkManager.Begin())
                        {
                            foreach (var concoursId in retourMajQualif.ListeIdConcoursQualifMaj)
                            {
                                messageService.Object.AjouterMessageSysteme(
                                new AjouterMessageInput
                                {
                                    ConcoursId = concoursId,
                                    Contenu = source.GetString("finQualifications")
                                });
                             }
    
                             unitOfWork.Complete();
                        }
                    }
    
                [...]
                }
            }
    
    [...]
    

    I hope it's not bad coding, and if it's not, that it could help someone else !

  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    While your code works, I want to make 2 suggestions for a better practice:

    1. If you need to IUnitOfWorkManager, inject it into your class and use it. Not use application service's object.
    2. Do not use application services from background jobs. App services has audit logging, validation, authorization... features you probably don't need in a background service. App service should be user by user interface. If you need logic in your app service, you can extract this logic into a domain service and use this domain service both from the app service and background job.
  • User Avatar
    0
    nicolas33 created

    Thank you for your answer Hikalkan.

    1. Honestly, I don't know how to do this, I don't fully understand how works all this injection principle with Castle Windsor. Could you point me to an example code of this ?

    2. What I try to acheive with HangFire is to use a background job instead of a windows service. Is it such a bad idea ? I need something which runs every x minutes, update some data in the database, then get data from it, and finally insert some data in. When you're talking about using a domain service where the logic would be, are you talking about the "Core" domain of the template ? You're meaning that my background job should "talk" directly to the "Core" instead of the application service ?