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.
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.
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.
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.
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:
I'm using version 8.14.1 for this, would this have an effect?
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?
I've tested the above sample code on a clean 8.14.1 install and it updated the data type configuration without any issues, so it must be something else in your code (e.g. not completing a scope, causing all changes to rollback).
You can indeed check the logs in Settings > Logs (or on-disk in App_Data\Logs). I checked the audit log and it data type saves aren't audited (they don't cause a row being added to umbracoAudit).
My advise would be to start debugging code and try to isolate what's causing this to not work, either by stripping out/disabling code from your existing solution or adding bits to a clean install and seeing when it starts/stops working...
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:
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:
It works on my ServerVariable.Parsing IComponent too:
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
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, butIComponent
s 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: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.
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:
Here's how I used it:
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.
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:
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.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:
Regards,
Francis
Hi Francis,
I've tested the above sample code on a clean 8.14.1 install and it updated the data type configuration without any issues, so it must be something else in your code (e.g. not completing a scope, causing all changes to rollback).
You can indeed check the logs in Settings > Logs (or on-disk in
App_Data\Logs
). I checked the audit log and it data type saves aren't audited (they don't cause a row being added toumbracoAudit
).My advise would be to start debugging code and try to isolate what's causing this to not work, either by stripping out/disabling code from your existing solution or adding bits to a clean install and seeing when it starts/stops working...
Thanks Ronald. I'll be sure to take a look and let you know.
is working on a reply...