Copied to clipboard

Flag this post as spam?

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


  • Francis 7 posts 77 karma points
    1 week ago
    Francis
    0

    IComponent Initialize() Not Working as Expected

    Hello,

    I'm wondering if you can help me out. I've setup an IComponent that does some simple tasks on initialize. In this example, I'm simply adding pre-values on a dropdown datatype:

    public class ContentNodes : IComponent
    {
        private readonly IContentService _contentService;
        private readonly IDataTypeService _dataTypeService;
        private readonly IUmbracoContextFactory _umbracoContextFactory;
        public ContentNodes(IContentService contentService, 
            IDataTypeService dataTypeService, 
            IUmbracoContextFactory umbracoContextFactory)
        {
            _contentService = contentService;
            _dataTypeService = dataTypeService;
            _umbracoContextFactory = umbracoContextFactory;
        }
    
        public void Initialize()
        {
    
            //populate dropdown
            var sampList = new List<ValueListConfiguration.ValueListItem>();
            sampList.Add(new ValueListConfiguration.ValueListItem
            {
                Id = 1,
                Value = "TEST VAL ONE"
            });
            sampList.Add(new ValueListConfiguration.ValueListItem
            {
                Id = 2,
                Value = "TEST VAL TWO"
            });
    
            var dropDownTest = _dataTypeService.GetDataType(1515);
            ((ValueListConfiguration)dropDownTest.Configuration).Items.AddRange(sampList);
            _dataTypeService.Save(dropDownTest);
    
        }
    
        public void Terminate()
        {
    
        }
    
    }
    

    This gets fired but nothing is happing. Meaning, the test values doesn't get added on my dropdown.

    What's strange is that I have another IComponent that extends Content Service Publish and this code works over there:

        public void Initialize()
        {
            ContentService.Published += ContentService_Published;
        }
    
        private void ContentService_Published(IContentService sender, ContentPublishedEventArgs contentPublishingEventArgs)
        {
    
            //populate dropdown
            var sampList = new List<ValueListConfiguration.ValueListItem>();
            sampList.Add(new ValueListConfiguration.ValueListItem
            {
                Id = 1,
                Value = "TEST VAL ONE"
            });
            sampList.Add(new ValueListConfiguration.ValueListItem
            {
                Id = 2,
                Value = "TEST VAL TWO"
            });
    
            var dropDownTest = _dataTypeService.GetDataType(1515);
            ((ValueListConfiguration)dropDownTest.Configuration).Items.AddRange(sampList);
            _dataTypeService.Save(dropDownTest);
    

    It works on my ServerVariable.Parsing IComponent too:

        public void Initialize()
        {
            ServerVariablesParser.Parsing += Variables_Parsing;
        }
    
        private void Variables_Parsing(IContentService sender, ContentPublishedEventArgs contentPublishingEventArgs)
        {
    
            //populate dropdown
            var sampList = new List<ValueListConfiguration.ValueListItem>();
            sampList.Add(new ValueListConfiguration.ValueListItem
            {
                Id = 1,
                Value = "TEST VAL ONE"
            });
            sampList.Add(new ValueListConfiguration.ValueListItem
            {
                Id = 2,
                Value = "TEST VAL TWO"
            });
    
            var dropDownTest = _dataTypeService.GetDataType(1515);
            ((ValueListConfiguration)dropDownTest.Configuration).Items.AddRange(sampList);
            _dataTypeService.Save(dropDownTest);
    

    So I'm not sure what I'm missing here. I cannot map this task to a service because I just need the pre-values to be populated on startup. I'm not sure if there is a service that does just that, load.

    I wonder if you can help.

    Thanks in advance

    Francis

  • Ronald Barendse 36 posts 202 karma points hq c-trib
    1 day ago
    Ronald Barendse
    0

    Hi Francis,

    The biggest difference between IComponent.Initialize and the event handlers is whether it's executed within a request or background context. Every request will automatically create a scope, but IComponents aren't executed in a request, but at application startup, so you'll have to manually create that scope.

    You can do this by injecting the IScopeProvider and wrapping your existing code with:

    using IScope scope = _scopeProvider.CreateScope();
    
    // Existing code
    
    scope.Complete();
    

    The event handlers in your example only happen to work when the event is fired withing a request, so if content is published from a background thread, the ContentService_Published also wouldn't work. Also, the scope ensures all write-operations are committed when you complete it (it starts a database transaction behind the scenes).

    TL;DR Make sure to create (and complete!) a scope when doing write operations.

  • Francis 7 posts 77 karma points
    1 day ago
    Francis
    0

    Hello Ronald,

    Thank you for the response. I've tried you're suggestion but still did not work. Here's how I added the scope provider:

        private readonly IContentService _contentService;
        private readonly IDataTypeService _dataTypeService;
        private readonly IUmbracoContextFactory _umbracoContextFactory;
        private readonly ILogger _logger;
        private readonly IScopeProvider _scopeProvider;
    
        public SampleComponent(IContentService contentService, 
            IDataTypeService dataTypeService, 
            IUmbracoContextFactory umbracoContextFactory, 
            ILogger logger,
            IScopeProvider scopeProvider)
        {
            _contentService = contentService;
            _dataTypeService = dataTypeService;
            _umbracoContextFactory = umbracoContextFactory;
            _logger = logger;
            _scopeProvider = scopeProvider;
        }
    

    Here's how I used it:

            using (IScope scope = _scopeProvider.CreateScope())
            {
                //populate dropdown - NOT WORKING
                var newValueList = new List<ValueListConfiguration.ValueListItem>();
                newValueList.Add(new ValueListConfiguration.ValueListItem
                {
                    Id = 1,
                    Value = "TEST VAL ONE111"
                });
                newValueList.Add(new ValueListConfiguration.ValueListItem
                {
                    Id = 2,
                    Value = "TEST VAL TWO222"
                });
    
                var dropDownTest = _dataTypeService.GetDataType(1515); //target dropdown datatype
                ((ValueListConfiguration)dropDownTest.Configuration).Items.AddRange(newValueList);
                _dataTypeService.Save(dropDownTest);
    
                scope.Complete();
                _logger.Info<ContentNodes>($"Dropdown Entries Added");
            }
    

    Breakpoint hits it and logs displays the logger message so I know this is being hit. However, the two values are still not being added to my target dropdown data type.

    Thank you again.

  • Ronald Barendse 36 posts 202 karma points hq c-trib
    17 hours ago
    Ronald Barendse
    1

    I've just tested this out on a clean Umbraco 8.18 site and looks like the data type service already creates its own scope, so that's not even required.

    The following code correctly replaces all existing items of the default dropdown data type:

    using System.Collections.Generic;
    using Umbraco.Core;
    using Umbraco.Core.Composing;
    using Umbraco.Core.Models;
    using Umbraco.Core.PropertyEditors;
    using Umbraco.Core.Services;
    
    public class ApplicationComposer : ComponentComposer<ApplicationComponent>, IUserComposer
    { }
    
    public class ApplicationComponent : IComponent
    {
        private readonly IDataTypeService _dataTypeService;
    
        public ApplicationComponent(IDataTypeService dataTypeService) => _dataTypeService = dataTypeService;
    
        public void Initialize()
        {
            var dataType = _dataTypeService.GetDataType(Constants.DataTypes.DropDownSingle);
    
            var items = new List<ValueListConfiguration.ValueListItem>()
                {
                    new ValueListConfiguration.ValueListItem
                    {
                        Id = 1,
                        Value = "TEST VAL ONE111"
                    },
                    new ValueListConfiguration.ValueListItem
                    {
                        Id = 2,
                        Value = "TEST VAL TWO222"
                    }
                };
    
            dataType.ConfigurationAs<ValueListConfiguration>().Items = items;
            _dataTypeService.Save(dataType);
        }
    
        public void Terminate()
        { }
    }
    

    It might be timing (hence the IUserComposer, so it runs after all core ones), custom logic cancelling save or some other issue, so make sure to check your logs and audit trail.

  • Francis 7 posts 77 karma points
    15 hours ago
    Francis
    0

    Hi Ronald,

    Thank you again for the response. I just tried this again using the code that you shared but it still did not work. I have 2 follow-up questions:

    1. I'm using version 8.14.1 for this, would this have an effect?
    2. For the logs I'm only able to check it using Settings--> logs ---> All logs and see my logger info there. Is there any other place I could be checking for the audit trail?

    Regards,

    Francis

Please Sign in or register to post replies

Write your reply to:

Draft