Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Roy Berris 89 posts 576 karma points c-trib
    Sep 29, 2021 @ 13:01
    Roy Berris
    0

    Access umbraco context in background thread

    Hello,

    We have a background task that runs a sync service. When this service is finished syncing I want to add / remove umbraco content items based on that sync. The problem is that because this task runs in the background and is a fire and forget, I cannot access the umbraco context of the created scope. Because this seems to be disposed.

    It works as follows. In the backoffice we have a button which calls an UmbracoAuthorizedApiController. This will run a Task fire and forget style.

    Now I either need to create own scope in this new thread? How do I do this, or what is another way to access the umbraco context from this thread so that I can add and remove IContent items using the IContentService?

  • Dan Diplo 1554 posts 6205 karma points MVP 5x c-trib
    Sep 29, 2021 @ 14:16
    Dan Diplo
    0

    The way I normally do syncs in Umbraco is to have a controller (could be surface or API) that can be called to trigger the sync. So your scheduled task makes an HTTP request to the controller endpoint (you can secure with a header or query string) and you set a long time out. That way you have access to the server HTTP Context and all the other contexts, such as Umbraco Context.

    Having said that, you shouldn't need to access the Umbraco context to use IContentService. You should be able to inject this in your own class and call it.

  • Roy Berris 89 posts 576 karma points c-trib
    Sep 29, 2021 @ 14:33
    Roy Berris
    0

    I can inject this, but when it reaches that code I get the message that context is not called from web request. Meaning the previous scope is disposed. Right now I have a workaround where I do not use a background thread but just run it from the API controller directly. Now the code does work, but ideally I want the handler (the controller) to instantly respond back to the caller, and execute the code in the background.

  • Dennis 75 posts 397 karma points MVP
    Sep 29, 2021 @ 14:33
    Dennis
    100

    What Dan says should work, but I would not recommend it, it's generally not recommended to create endpoints with long response times (I remember reading this somewhere, though I couldn't find the source, so verification required). That being said, fire and forget tasks are also generally not recommended. Instead, one should create a background worker that can perform your synchronisation.

    Getting back to your question though: You can easily create your own scope with the IUmbracoContextFactory like so:

    public class MyService
    {
        private readonly IUmbracoContextFactory _umbraco;
        public MyService(IUmbracoContextFactory umbraco) // injected here with Dependency Injection
        {
            _umbraco = umbraco;
        }
    
        public Task MyActionAsync()
        {
            using(var cref = _umbraco.EnsureUmbracoContext())
            {
                // your logic here
            }
        }
    }
    
  • Lucas Michaelsen 32 posts 232 karma points
    Sep 29, 2021 @ 14:38
    Lucas Michaelsen
    0

    Hello Roy

    I have just been in the same case, where with the background each 30 min, I need to add/edit or delete data.

    I have the task look like this:

    [RuntimeLevel(MinLevel = Umbraco.Core.RuntimeLevel.Run)]
    public class TestComposer : ComponentComposer<TestCommponent>
    {
    }
    
    
    public class TestCommponent: IComponent
    {
        private readonly IProfilingLogger _logger;
        private readonly IRuntimeState _runtime;
        private readonly IContentService _contentService;
        private readonly IUmbracoContextFactory _contextFactory;
        private readonly BackgroundTaskRunner<IBackgroundTask> _backgroundTask;
    
        public TestCommponent(IProfilingLogger logger, IRuntimeState runtime, IContentService consentService, IUmbracoContextFactory contextFactory)
        {
            _logger = logger;
            _runtime = runtime;
            _contentService = consentService;
            _contextFactory = contextFactory;
            _backgroundTask = new BackgroundTaskRunner<IBackgroundTask>("testtask", _logger);
        }
    
        public void Initialize()
        {
            _backgroundTask.TryAdd(new TestJob(_backgroundTask, 10000, 300000, _runtime, _logger, _contentService, _contextFactory));
        }
    
        public void Terminate()
        {
        }
    }
    
    public class TestJob: RecurringTaskBase
    {
        private readonly IRuntimeState _runtime;
        private readonly IProfilingLogger _logger;
        private readonly IContentService _contentService;
        private readonly IUmbracoContextFactory _contextFactory;
    
        public TestJob(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliSecond, IRuntimeState runtime, IProfilingLogger logger, IContentService contentService, IUmbracoContextFactory contextFactory) 
            : base (runner, delayMilliseconds, periodMilliSecond)
        {
            _runtime = runtime;
            _logger = logger;
            _contentService = contentService;
            _contextFactory = contextFactory;
        }
    
        public override bool IsAsync => false;
    
        public override bool PerformRun()
        {
    
                        // NEEDED!!!! Without UmbracoContext a Umbraco internal exception is fired.
                        using (_contextFactory.EnsureUmbracoContext())
                        {
                            _contentService.SaveAndPublish(item, q);
                        }
        }
    }
    
Please Sign in or register to post replies

Write your reply to:

Draft