Copied to clipboard

Flag this post as spam?

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


  • Heather Floyd 604 posts 1002 karma points MVP 5x c-trib
    Sep 15, 2020 @ 22:20
    Heather Floyd
    0

    Utilizing UmbracoHelper in Content/Media events

    [Umbraco 8.7.0]

    I would like to use UmbracoHelper to lookup some published content when saving a media node (in order to update some custom properties on the media node), but the question would generally apply to any instance where we have custom event code running and would like to access UmbracoHelper or other services, etc.

    The summary of the long discussion over here: Using UmbracoHelper in a custom class in v8:

    Though EASY, it is generally considered bad form to do this:

    var helper = Umbraco.Web.Composing.Current.UmbracoHelper;
    

    So, based on the documentation here: Injecting Services into a Component, I tried to do something better. Here is my code setup:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Umbraco.Core;
    using Umbraco.Core.Composing;
    using Umbraco.Core.Events;
    using Umbraco.Core.Models;
    using Umbraco.Core.Models.PublishedContent;
    using Umbraco.Core.Services;
    using Umbraco.Core.Services.Implement;
    using Umbraco.Web;
    
    public class RegisterEventsComposer : ComponentComposer<RegisterEventsComponent>
    {
        //Events added via RegisterEventsComponent  
    }
    
    [RuntimeLevel(MinLevel = RuntimeLevel.Run)]
    public class RegisterEventsComponent : IComponent
    {
        private readonly UmbracoHelper _umbracoHelper;
    
        public RegisterEventsComponent(UmbracoHelper umbracoHelper)
        {
            _umbracoHelper = umbracoHelper;
        }
    
        public void Initialize()
        {
            // ApplicationStarted event in V7: add your events here
    
            //Events
            MediaService.Saving += MediaService_Saving;
        }
    
        public void Terminate()
        {
            // called when the Umbraco application shuts down.
            MediaService.Saving -= MediaService_Saving;
        }
    
        private void MediaService_Saving(IMediaService sender, SaveEventArgs<IMedia> e)
        {
            foreach (var doc in e.SavedEntities)
            {
                //By Specific MediaType
                switch (doc.ContentType.Alias)
                {
                    case "myMediaTypeAlias":
                        //Do stuff using _umbracoHelper here
                        break;
    
                    default:
                        //Nothing
                        break;
                }
            }
        }
    }
    

    This compiles just fine, but when I run the site, it blows up with a boot error:

    Server Error in '/' Application.
    Boot failed: Umbraco cannot run. See Umbraco's log file for more details.
    
    -> Umbraco.Core.Exceptions.BootFailedException: Boot failed.
    
    -> System.NullReferenceException: Object reference not set to an instance of an object.
      at Umbraco.Web.Runtime.WebInitialComposer.<>c.<Compose>b__0_6(IFactory factory) in D:\a\1\s\src\Umbraco.Web\Runtime\WebInitialComposer.cs:line 121
      at DynamicMethod(Object[] )
      at LightInject.PerContainerLifetime.GetInstance(Func`1 createInstance, Scope scope) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 6169
      at LightInject.ServiceContainer.EmitLifetime(ServiceRegistration serviceRegistration, Action`1 emitMethod, IEmitter emitter) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 4656
      at LightInject.ServiceContainer.<>c__DisplayClass153_0.<CreateEmitMethodWrapper>b__0(IEmitter ms) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 3856
      at LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action`1 serviceEmitter) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 3776
      at LightInject.ServiceContainer.CreateDelegate(Type serviceType, String serviceName, Boolean throwError) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 4743
      at LightInject.ServiceContainer.CreateDefaultDelegate(Type serviceType, Boolean throwError) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 4705
      at LightInject.ServiceContainer.GetInstance(Type serviceType) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 3437
      at Umbraco.Core.Composing.ComponentCollectionBuilder.CreateItem(IFactory factory, Type itemType) in D:\a\1\s\src\Umbraco.Core\Composing\ComponentCollectionBuilder.cs:line 33
      at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
      at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
      at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
      at Umbraco.Core.Composing.ComponentCollectionBuilder.CreateItems(IFactory factory) in D:\a\1\s\src\Umbraco.Core\Composing\ComponentCollectionBuilder.cs:line 25
      at Umbraco.Core.Composing.CollectionBuilderBase`3.CreateCollection(IFactory factory) in D:\a\1\s\src\Umbraco.Core\Composing\CollectionBuilderBase.cs:line 120
      at LightInject.PerContainerLifetime.GetInstance(Func`1 createInstance, Scope scope) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 6169
      at LightInject.ServiceContainer.EmitLifetime(ServiceRegistration serviceRegistration, Action`1 emitMethod, IEmitter emitter) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 4656
      at LightInject.ServiceContainer.<>c__DisplayClass153_0.<CreateEmitMethodWrapper>b__0(IEmitter ms) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 3856
      at LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action`1 serviceEmitter) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 3776
      at LightInject.ServiceContainer.CreateDelegate(Type serviceType, String serviceName, Boolean throwError) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 4743
      at LightInject.ServiceContainer.CreateDefaultDelegate(Type serviceType, Boolean throwError) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 4705
      at LightInject.ServiceContainer.GetInstance(Type serviceType) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 3437
      at Umbraco.Core.FactoryExtensions.GetInstance[T](IFactory factory) in D:\a\1\s\src\Umbraco.Core\FactoryExtensions.cs:line 23
      at Umbraco.Core.Runtime.CoreRuntime.Boot(IRegister register, DisposableTimer timer) in D:\a\1\s\src\Umbraco.Core\Runtime\CoreRuntime.cs:line 187
    

    Though it isn't clearly specified, I assume the issue is that at boot, UmbracoContext isn't ready... or something like that.

    If I replace:

    ...
        public RegisterEventsComponent(UmbracoHelper umbracoHelper)
        {
            _umbracoHelper = umbracoHelper;
        }
    ...
    

    with

    ...
        public RegisterEventsComponent()
        {
    
        }
    ...
    

    The boot error goes away, but of course, I don't have my UmbracoHelper now.

    So, what is the proper way to do this?

  • Markus Johansson 1911 posts 5735 karma points MVP c-trib
    Sep 16, 2020 @ 04:57
    Markus Johansson
    100

    Hi!

    I've also struggled with injecting the UmbracoHelper, especially for methods that might run in background-threads (ie when populating examine-indexes etc).

    I've found that the way to go is to use the "IUmbracoContextFactory" and access the content-cache this way, this works in all the places that I've needed access to the cache, custom events, controllers, examine population and also in background-jobs (like when we're using Hangfire to do stuff).

    So in our solutions we always use the IContextFactory in any "repository" that needs access to Umbraco to create our domain models.

    There is some info about this in the docs under "Custom Services and Helpers" on the page that you also linked to: https://our.umbraco.com/documentation/Implementation/Services/

    Example with your code:

      public class RegisterEventsComponent : IComponent
      {
      private readonly IUmbracoContextFactory _umbracoContextFactory;
    
    public RegisterEventsComponent(IUmbracoContextFactory umbracoContextFactory)
    {
        _umbracoContextFactory = umbracoContextFactory;
    }
    
    public void Initialize()
    {
        //Events
        MediaService.Saving += MediaService_Saving;
    }
    
    public void Terminate()
    {
        // called when the Umbraco application shuts down.
        MediaService.Saving -= MediaService_Saving;
    }
    
    private void MediaService_Saving(IMediaService sender, SaveEventArgs<IMedia> e)
    {
       using(var ctx = _umbracoContextFactory.EnsureUmbracoContext())
      {
    
          foreach (var doc in e.SavedEntities)
          {
             //By Specific MediaType
              switch (doc.ContentType.Alias)
              {
                case "myMediaTypeAlias":
                    //Do stuff using ctx here
                    var node = ctx.UmbracoContext.Content.GetById<Article>(id);
                    break;
    
                default:
                    //Nothing
                    break;
            }
        }
      }
    }
    
  • Heather Floyd 604 posts 1002 karma points MVP 5x c-trib
    Sep 16, 2020 @ 16:45
    Heather Floyd
    0

    Thanks, Markus! That worked great :-)

Please Sign in or register to post replies

Write your reply to:

Draft