Copied to clipboard

Flag this post as spam?

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


  • Tiago Braz 9 posts 91 karma points
    Feb 28, 2019 @ 13:53
    Tiago Braz
    0

    Umbraco 8 and Hangfire

    Hi,

    I'm migrating a project from 7.X to 8 in order to familiarize myself with this brand new Umbraco version!

    Happens that this project relies on background jobs (using Hangfire) for some recurring tasks, and those tasks need to access IPublishedContent.

    In the previous version faking, an UmbracoContext was somewhat easy with a EnsureUmbracoContext method that would create a context using UmbracoContext.EnsureContext if UmbracoContext.Current was null and it worked pretty well.

    With Umbraco8 and all the changes under the hood I have yet to figure out a way to access/create a UmbracoContext and/or IUmbracoContextAccessor for those cases or event setup a LightInject container within my Hangfire server to with all the dependencies resolved.

    I have been fiddling in Umbraco source code in order to try to figure a way but without success so far.

    Can anybody point me in the right direction?

    Thanks

    P.S.: I know that an easy way to solve this would be to have an API endpoint and make my background job call that endpoint, solving my issue. But I'm not interested in such inelegant solution.

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Feb 28, 2019 @ 14:00
    Sebastiaan Janssen
    0

    For the quick-and-dirty solution:

    using Umbraco.Web.Composing; and use Current.UmbracoContext.what_you_need.

    Don't forget to Install-Package UmbracoCms.Web, which is a new NuGet package that we didn't have for v7.

  • Tiago Braz 9 posts 91 karma points
    Feb 28, 2019 @ 14:04
    Tiago Braz
    0

    Thanks for the quick reply! H5YR!

    I will definitively try that.

    But, will the UmbracoContext be setup in this case? Since there is no HttpRequest associated with my Job?

    Well, going to try that out and will be back with an update.

    Thanks again!

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Feb 28, 2019 @ 14:46
    Sebastiaan Janssen
    0

    Yeah.. that is a really good question and I'm not sure of that either. Give it a go and see how far you get.

    For future improvements I'm curious as to what exactly you're trying to access that need a request context, might be able to figure it out with you!

  • Sebastiaan Janssen 5060 posts 15522 karma points MVP admin hq
    Feb 28, 2019 @ 22:27
    Sebastiaan Janssen
    105

    Might be of interest, just blogged about using Hangfire with Umbraco 8: https://cultiv.nl/blog/using-hangfire-for-scheduled-tasks-in-umbraco/

  • Tiago Braz 9 posts 91 karma points
    Mar 04, 2019 @ 10:22
    Tiago Braz
    0

    Yes thank you, somebody actually already linked that blog post in our slack. And its exactly what I needed! Marking your answer as the accepted one.

    Cheers!

  • Tiago Braz 9 posts 91 karma points
    Mar 08, 2019 @ 18:47
    Tiago Braz
    0

    Hey Sebastian,

    So I've been playing with this on my spare time and while I now have hangfire working based on the blog you shared I'm still blocked.

    When in jobs (or registering the jobs) Umbraco.Web.Composing.Current has some of the key elements to null (UmbracoContext, UmbracoHelper, ...) and this is kind of expected, since there is no HttpRequest tied to them.

    But Umbraco.Core.Composing.Current has all the values set, and the Factory returns a lot of instances. But the one I really want is not there.

    I really would like my background job to access IPublishContent, for example, I have a settings DocType/Node with some settings that I need to access so I really would like a way to load that Node from NuCache so I know that I have what is actually published (and not IContent that might have changes that I don't want, since they are not published).

    I probably could use IContent and the versions to find the latest one that is/was published (?) ... but is this the better/only way to do it?

    I believe Umbraco.Core.Composing.Current should have a way to access the an instace of IPublishedCache.

    Thanks

  • Alessandro 11 posts 84 karma points
    Apr 03, 2019 @ 16:00
    Alessandro
    0

    Thank you Sebastiaan,

    and what about IoC dependencies injected in the hangfire job class?

    I see Hangfire has Hangfire.LightInject project but I have two problems:

    • how can I get the current LightInject.ServiceContainer from the core?
    • where should i call the UseLightInjectActivator method to be sure that all the core dependencies are injected?
  • Tiago Braz 9 posts 91 karma points
    Apr 03, 2019 @ 16:55
    Tiago Braz
    1

    Hey!

    I'm not sure that my response is the correct one, but this is how I solved that issue:

    Umbraco.Core.Composing.Current.Factory is your container.

    So:

    Current.Factory.GetInstance<ILogger>()
    

    Will give you the logger.

    1. I created a separate Composition for my Hangfire, because I want some services to have a different lifespan than in Umbraco. I also created a CustomJobActivator and told Hangfire about it.

    The sample code looks something like this:

    // in your hangfire setup function
       GlobalConfiguration.Configuration.UseActivator(new UmbracoJobActivator(GetFactoryForJobComposition()));
       ...
    }
    
    private IFactory GetFactoryForJobComposition()
    {
        // This is needed because we want to use a different IoC container than the one we use in Umbraco, specially because of the lifetime of certain objects
        var composition = new Composition(RegisterFactory.Create(), Current.TypeLoader, Current.ProfilingLogger, Current.RuntimeState);
        composition = RegisterCompositionServices(composition);
    
        return composition.CreateFactory();
    }
    
    private Composition RegisterCompositionServices(Composition composition)
    {
        // because i want logging in my jobs
        composition.Register(_ => Current.Factory.GetInstance<ILogger>());
        // because i want profiling logging in my jobs
        composition.Register(_ => Current.Factory.GetInstance<IProfilingLogger>());
       // here you should register anything else you might need
       // now register your jobs    
        composition.Register(typeof(SomeHangFireJob));
        return composition;
    }
    
    // Job Activator class declaration
    internal class UmbracoJobActivator : JobActivator
    {
        private readonly IFactory Factory;
    
        public UmbracoJobActivator(IFactory factory)
        { Factory = factory; }
    
        public override object ActivateJob(Type jobType)
        {
            return Factory.GetInstance(jobType);
        }
    }
    

    Bear in mind that Current is Umbraco.Core.Composition.Current not Umbraco.Web.Composition.Current

    With this, all my jobs are activating correctly and the injection is working perfectly.

  • Stephen 767 posts 2273 karma points c-trib
    Mar 11, 2019 @ 09:08
    Stephen
    0

    Quick answer: there is a non-hackish way to access the front-end cache from within a background task - going to document it soon as I can.

  • Tiago Braz 9 posts 91 karma points
    Mar 11, 2019 @ 10:32
    Tiago Braz
    0

    Thank you Stephan!

    I'll be waiting for your answer!

  • Stephen 767 posts 2273 karma points c-trib
    Mar 11, 2019 @ 11:02
    Stephen
    4

    More details: the UmbracoContext is internally managed by an IUmbracoContextFactory - in most cases, you should not bother about it, but in your case, it's going to help.

    There is, unfortunately, no Current.UmbracoContextFactory at the moment, though we may add it. So you will have to get the factory via:

    var umbracoContextFactory = Current.Factory.GetInstance<IUmbracoContextFactory>();
    

    (that is, assuming you cannot inject it - of course, ideally, you should inject it)

    That factory can give you a reference to an UmbracoContext:

    using (var contextReference = umbracoContextFactory.EnsureUmbracoContext())
    {
        var umbracoContext = contextReference.UmbracoContext;
    }
    

    What happens is: if there already is a "current" UmbracoContext, the reference will point to it. Otherwise, a new one will be created, and is registered as the curent one - until the reference is disposed, and then the context is disposed and is not current anymore.

    Therefore, this is safe to use both within normal requests, and within background tasks.

    You can access the context via the contextReference.UmbracoContext property but, since it's also the current one, Current.UmbracoContext will return it, and anything that requires a context will work.

    Then, you can directly access umbracoContext.ContentCache to retrieve whatever you need.

    Making sense?

  • Tiago Braz 9 posts 91 karma points
    Mar 11, 2019 @ 11:12
    Tiago Braz
    0

    Yes! This should do the trick!

    I'm going to try it later this week and will give you feedback then!

    Thanks a lot for your help!

  • Tiago Braz 9 posts 91 karma points
    Mar 14, 2019 @ 16:55
    Tiago Braz
    1

    Works flawlessly! Thank you so much!

  • Markus Johansson 1936 posts 5864 karma points MVP 2x c-trib
    Sep 17, 2019 @ 15:29
    Markus Johansson
    0

    Hi Tiago!

    I've looked at the samples that you provided, as far as I understand you need to read content from the cache in one of you jobs right?

    Did you manage to get this working inside the job? Would you say that it's stable and "running fine"?

    I would love know more about how you did this since I'm faced with the exact same issue in a project right now.

    Any points in the right direction would be very much appreciated.

    Cheers!

  • David Zweben 268 posts 753 karma points
    Apr 23, 2019 @ 20:42
    David Zweben
    0

    I had trouble setting up Hangfire's IDashboardAuthorizationFilter for v8, so I wanted to share the code that ended up working for me:

    public class UmbracoAuthorizationFilter : IDashboardAuthorizationFilter
    {
        public bool Authorize([NotNull] DashboardContext context)
        {
            var auth = new HttpContextWrapper(HttpContext.Current).GetUmbracoAuthTicket();
    
            UmbracoBackOfficeIdentity user = UmbracoBackOfficeIdentity.FromClaimsIdentity(auth.Identity);
    
            if (user.Roles.Contains("admin"))
            {
                return true;
            }
            return false;
        }
    }
    

    The key part is UmbracoBackOfficeIdentity.FromClaimsIdentity(auth.Identity). In v8, from the context of Owin startup, the type you get back from GetUmbracoAuthTicket().Identity is System.Security.Claims.ClaimsIdentity, which doesn't contain a Roles property. That line will convert it to the Umbraco-specific type that does contain Roles.

  • Russell 11 posts 83 karma points c-trib
    Jul 31, 2019 @ 23:50
  • Nik 1612 posts 7258 karma points MVP 7x c-trib
    Nov 27, 2019 @ 11:53
    Nik
    1

    Attention

    Anyone who comes here with DI related issues trying to use the built in Light Inject container for Umbraco v8 this thread here talks about various ways around the DI issues with things like HTTP context not being available:

    https://our.umbraco.com/forum/umbraco-8/96445-hangfire-dependency-injection

  • Jonathon Cove 26 posts 101 karma points
    Jun 13, 2023 @ 11:17
    Jonathon Cove
    0

    I had a strange issue where Hangfire installed but the dashboard could not load any css or js, despite these being available if I navigated to their URLs.

    In that case, the fix was to add hangfire to the reserved paths in Umbraco. In the Web.Config:

    <add key="Umbraco.Core.ReservedPaths" value="~/hangfire" />
    
Please Sign in or register to post replies

Write your reply to:

Draft