AmbientContext error when trying to use ConentServ...
# help-with-umbraco
w
Has anyone come across this error before?
Copy code
System.AggregateException: One or more errors occurred. (No AmbientContext was found.) ---> System.InvalidOperationException: No AmbientContext was found.    at Umbraco.Cms.Infrastructure.Scoping.AmbientScopeContextStack.Pop()    at Umbraco.Cms.Infrastructure.Scoping.ScopeProvider.PopAmbientScopeContext()    at Umbraco.Cms.Infrastructure.Scoping.Scope.<>c__DisplayClass100_0.<RobustExit>g__HandleScopeContext|2()    at Umbraco.Cms.Infrastructure.Scoping.Scope.TryFinally(Action[] actions)    --- End of inner exception stack trace ---    at Umbraco.Cms.Infrastructure.Scoping.Scope.TryFinally(Action[] actions)    at Umbraco.Cms.Infrastructure.Scoping.Scope.RobustExit(Boolean completed, Boolean onException)    at Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()    at Umbraco.Cms.Infrastructure.Scoping.Scope.Dispose()    at Umbraco.Cms.Infrastructure.Examine.ExamineUmbracoIndexingHandler.DeferedReIndexForContent.<>c__DisplayClass6_0.<Execute>b__0(CancellationToken cancellationToken)    at Umbraco.Cms.Infrastructure.HostedServices.QueuedHostedService.BackgroundProcessing(CancellationToken stoppingToken)
Trying to run a Hangfire task to update a node with the ContentService. Have tried a million things regarding scopes etc.
This is with Umbraco 10.
s
If not: I've set it all up for you to be the most likely to be successful, use my package! šŸ˜… Or copy some of my code from the package.
w
I've implemented it manually, with a few improvements. Also followed your article https://cultiv.nl/blog/using-hangfire-to-update-umbraco-content/
s
Then, once you have done that, the proper way to use
ContentService
is described here: https://cultiv.nl/blog/using-hangfire-to-update-umbraco-content/
Okay, in that case, you're doing something strange.. no idea lol
w
My wife says that a lot...
s
Show us the code from your hangfire task, might be able to spot it
What are the improvements and can I incorporate them? 😁
w
Yes, I wanted to fork it when I had time. Making a few more configurations manageable. (Hangfire server/connection string possibly? can't remember exactly)
s
Cool cool! I'd love the help!
w
I've replciated your code from the article and get the same error.
Copy code
csharp
using Application.Core.OpeningHours.BankHolidays;
using Application.Hangfire;
using Hangfire;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.Composing;

namespace Application.Core.OpeningHours;

public class OpeningHoursComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        builder.Services.AddTransient<BankHolidayImportTask>();

        RunHangfire(builder);
    }

    private void RunHangfire(IUmbracoBuilder builder)
    {
        // Extra check whether Hangfire is enabled otherwise it will fail as the composer still tries to register the jobs
        builder.Services.Configure<HangfireOptions>(options =>
            builder.Config.GetSection(HangfireOptions.ConfigurationKey).Bind(options));

        var settings = builder.Config.GetSection(HangfireOptions.ConfigurationKey).Get<HangfireOptions>();

        if (settings == null || settings.Enabled == false)
            return;
        
        // If there's no connection string, e.g. Umbraco hasn't been installed yet, skip as the composer still tries to register the jobs
        var connectionString = builder.Config.GetConnectionString(settings.ConnectionStringName);
        if (string.IsNullOrEmpty(connectionString))
            return;
        
        // Schedule the ImportBankHolidays method to run on the 1st of every month
        RecurringJob.AddOrUpdate<BankHolidayImportTask>(
            "import-bank-holidays",
            x => x.ImportBankHolidays(),
            Cron.Minutely());
    }
}
(You might spot a couple of the Hangifre updates too!)
Copy code
csharp
namespace Application.Core.OpeningHours.BankHolidays;

public class BankHolidayImportTask
{
    private readonly IUmbracoContextFactory _umbracoContextFactory;
    private readonly IContentService _contentService;
    private readonly IServerMessenger _serverMessenger;
    private readonly IServiceProvider _serviceProvider;

    public BankHolidayImportTask(
        IUmbracoContextFactory umbracoContextFactory,
        IContentService contentService,
        IServiceProvider serviceProvider,
        IServerMessenger serverMessenger)
    {
        _umbracoContextFactory = umbracoContextFactory;
        _contentService = contentService;
        _serverMessenger = serverMessenger;
        _serviceProvider = serviceProvider;
    }

    public async Task ImportBankHolidays()
    {
        using var backgroundScope = new BackgroundScope(_serverMessenger);
        using var _ = _umbracoContextFactory.EnsureUmbracoContext();
        using var serviceScope = _serviceProvider.CreateScope();

        var query = serviceScope.ServiceProvider.GetRequiredService<IPublishedContentQuery>();
        var rootNode = query.ContentAtRoot().FirstOrDefault();

        if (rootNode == null)
            return;

        // Do something with ContentService
        var content = _contentService.GetById(rootNode.Id);
        content.SetValue("phoneNumber", DateTime.Now.ToString());
        _contentService.SaveAndPublish(content);
    }

    public class BackgroundScope : IDisposable
    {
        private readonly IServerMessenger _serverMessenger;

        public BackgroundScope(IServerMessenger serverMessenger)
        {
            _serverMessenger = serverMessenger;
        }

        public void Dispose()
        {
            if (_serverMessenger is BatchedDatabaseServerMessenger batchedDatabaseServerMessenger)
            {
                batchedDatabaseServerMessenger.SendMessages();
            }
        }
    }
}
The full error: 4 May 2024 13:10:54.949 Document (id=1151) has been published. 14 May 2024 13:10:56.593 Error occurred executing workItem.
Copy code
SourceContext Umbraco.Cms.Infrastructure.HostedServices.QueuedHostedService
ThreadId 9
WorkItem workItem
System.AggregateException: One or more errors occurred. (No AmbientContext was found.)
 ---> System.InvalidOperationException: No AmbientContext was found.
   at Umbraco.Cms.Infrastructure.Scoping.AmbientScopeContextStack.Pop()
   at Umbraco.Cms.Infrastructure.Scoping.ScopeProvider.PopAmbientScopeContext()
   at Umbraco.Cms.Infrastructure.Scoping.Scope.<>c__DisplayClass100_0.<RobustExit>g__HandleScopeContext|2()
   at Umbraco.Cms.Infrastructure.Scoping.Scope.TryFinally(Action[] actions)
   --- End of inner exception stack trace ---
   at Umbraco.Cms.Infrastructure.Scoping.Scope.TryFinally(Action[] actions)
   at Umbraco.Cms.Infrastructure.Scoping.Scope.RobustExit(Boolean completed, Boolean onException)
   at Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()
   at Umbraco.Cms.Infrastructure.Scoping.Scope.Dispose()
   at Umbraco.Cms.Infrastructure.Examine.ExamineUmbracoIndexingHandler.DeferedReIndexForContent.<>c__DisplayClass6_0.<Execute>b__0(CancellationToken cancellationToken)
   at Umbraco.Cms.Infrastructure.HostedServices.QueuedHostedService.BackgroundProcessing(CancellationToken stoppingToken)
The SaveAndPublish works, it updates the index and cache, but something in the HostedServices throws a scope tantrum.
s
Copy code
cs
var x = "test";
ā˜ļø Tip: You can make it more readable with a language definition! (please? 😁 )
excellent, thank you!
j
You can simplify the part where you get the rootnodes to just use the umbraco context which you are getting and then assigning to a throwaway var.
Copy code
csharp
using var context = _umbracoContextFactory.EnsureUmbracoContext();

var rootNode = context.UmbracoContext.Content.GetAtRoot().FirstOrDefault();

if (rootNode == null)
    return;
s
I wonder if you actually have to add the task as
AddScoped
like in the sample code
Copy code
cs
builder.Services.AddScoped<IJobs, Jobs>();
Instead of adding it as
Transient
.
I think that only works if the root content is currently published? Might work actually 😁
j
True but so would IPublishedContentQuery which he is currently using šŸ™‚ The note on https://docs.umbraco.com/umbraco-cms/implementation/services#custom-services-and-helpers makes me think that IPublishedContentQuery is not available outside of HTTP requests
w
I've lifted that off of Seb's article, which is replicating the error. In my real code I'm just using the ContentService to get the node out and update.
s
It certainly is availble! But only if you construct everything in this exact way šŸ˜…
Don't. That's not going to work 😁
But my sample code doesn't work either, so that's curious.
w
Why not?
s
> It's not difficult to inject an IContentService now and get rolling. But we've been getting sporadic reports of the content cache and Examine indexes not always updating accordingly when doing this from a background task. > > To help with this problem, community member Chad (also known as nzdev) recently posted some code on our Discord server that makes sure that the DatabaseServerMessenger sends the message that content has been updated. > > So here's some updated code that doesn't just access the content cache but also saves and publishes our home page:
> Just to make it more clear, we're now also injecting IServerMessenger and IContentService, the former to make sure we can create the BackgroundScope and the latter to be able to update the content.
w
I'd originally tried using the cache to get the node first, then just use the Content Service to update, but same issue.
s
@Wiggy can you try to register it as Scoped? ā˜ļø
w
I haven't seen any of those sporadic issues. I've also tried supressing events and reindexing manually and you don't get the ambientscope error, but the cache is not updated.
Trying now!
Same thing happens.
Copy code
System.AggregateException: One or more errors occurred. (No AmbientContext was found.)
 ---> System.InvalidOperationException: No AmbientContext was found.
   at Umbraco.Cms.Infrastructure.Scoping.AmbientScopeContextStack.Pop()
   at Umbraco.Cms.Infrastructure.Scoping.ScopeProvider.PopAmbientScopeContext()
   at Umbraco.Cms.Infrastructure.Scoping.Scope.<>c__DisplayClass100_0.<RobustExit>g__HandleScopeContext|2()
   at Umbraco.Cms.Infrastructure.Scoping.Scope.TryFinally(Action[] actions)
   --- End of inner exception stack trace ---
   at Umbraco.Cms.Infrastructure.Scoping.Scope.TryFinally(Action[] actions)
   at Umbraco.Cms.Infrastructure.Scoping.Scope.RobustExit(Boolean completed, Boolean onException)
   at Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()
   at Umbraco.Cms.Infrastructure.Scoping.Scope.Dispose()
   at Umbraco.Cms.Infrastructure.Examine.ExamineUmbracoIndexingHandler.DeferedReIndexForContent.<>c__DisplayClass6_0.<Execute>b__0(CancellationToken cancellationToken)
   at Umbraco.Cms.Infrastructure.HostedServices.QueuedHostedService.BackgroundProcessing(CancellationToken stoppingToken)
s
@Jemayn this indeed works just as well!
Copy code
cs
    public void ManipulateContent(PerformContext context)
    {
        using var backgroundScope = new BackgroundScope(_serverMessenger);
        using var umbracoContext = _umbracoContextFactory.EnsureUmbracoContext();
        using var serviceScope = _serviceProvider.CreateScope();

        var rootNode = umbracoContext.UmbracoContext.Content?.GetAtRoot().FirstOrDefault();

        if (rootNode == null) return;
j
I've started always using the umbracoContext factory as that seems to be the only one always working šŸ˜… Don't know if it will solve this specific problem though
s
It doesn't seem like it would šŸ˜…
Afraid all I'm able to produce is:
Copy code
System.InvalidOperationException: Cannot save a non-current version.
   at Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement.DocumentRepository.PersistUpdatedItem(IContent entity)
   at Umbraco.Cms.Core.Cache.DefaultRepositoryCachePolicy`2.Update(TEntity entity, Action`1 persistUpdated)
   at Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement.EntityRepositoryBase`2.Save(TEntity entity)
   at Umbraco.Cms.Core.Services.ContentService.<>c__DisplayClass64_0.<CommitDocumentChangesInternal>g__SaveDocument|2(IContent c)
   at Umbraco.Cms.Core.Services.ContentService.CommitDocumentChangesInternal(ICoreScope scope, IContent content, EventMessages eventMessages, IReadOnlyCollection`1 allLangs, IDictionary`2 notificationState, Int32 userId, Boolean branchOne, Boolean branchRoot)
   at Umbraco.Cms.Core.Services.ContentService.SaveAndPublish(IContent content, String culture, Int32 userId)
   at v100test.BankHolidayImportTask.ImportBankHolidays() in E:\Dev\Temp\v100test\HangfireScheduler.cs:line 56
Which happens when the task runs when another update is still in progress. The code is now almost exactly the same as my packaged code.. The package code doesn't need this any more by the way:
// Extra check whether Hangfire is enabled otherwise it will fail as the composer still tries to register the jobs
. I've added the solution for you to fiddle with! https://cdn.discordapp.com/attachments/1239907055725449297/1239926093197086750/v100test.zip?ex=6644b2a9&is=66436129&hm=aab4ed36b71589707cd3c7083a58cffb91bf6a038a777310dfa7ffd053f384b3&
I don't know when you forked the package @Wiggy - but maybe try to comment out your custom code for it and install the package again and see what happens? 🤷
w
It doesn't feel like a Hangfire / package issue. Running bog statndard Console.Writeline("Blob") tasks works fine.
s
Well that doesn't involve anything Umbraco so I'd expect it to run fine šŸ˜…
123 Views