Skip to content

Instantly share code, notes, and snippets.

@sniffdk
Last active February 28, 2022 10:03
Show Gist options
  • Star 27 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save sniffdk/7600822 to your computer and use it in GitHub Desktop.
Save sniffdk/7600822 to your computer and use it in GitHub Desktop.
Fake an UmbracoContext for use when doing published scheduling or other scenarios where UmbracoContext is normally null.
public class ContextHelpers
{
public static UmbracoContext EnsureUmbracoContext() {
if (UmbracoContext.Current != null)
{
return UmbracoContext.Current;
}
var httpContext = new HttpContextWrapper(HttpContext.Current ?? new HttpContext(new SimpleWorkerRequest("temp.aspx", "", new StringWriter())));
/* v7.3+ */
return UmbracoContext.EnsureContext(
httpContext,
ApplicationContext.Current,
new WebSecurity(httpContext, ApplicationContext.Current),
UmbracoConfig.For.UmbracoSettings(),
UrlProviderResolver.Current.Providers,
false);
/* v6.1.4 - v7.2.8 */
return UmbracoContext.EnsureContext(httpContext, ApplicationContext.Current, new WebSecurity(httpContext, ApplicationContext.Current), false);
/* v6.1.3 and earlier (I think) */
return (typeof(UmbracoContext)
.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
.First(x => x.GetParameters().Count() == 3)
.Invoke(null, new object[] { httpContext, ApplicationContext.Current, false })) as UmbracoContext;
}
}
public class EventHooks : ApplicationEventHandler
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
base.ApplicationStarted(umbracoApplication, applicationContext);
Umbraco.Core.Services.ContentService.Published += (sender, args) =>
{
using (var umbracoContext = SiteHelpers.EnsureUmbracoContext())
{
var helper = new UmbracoHelper(umbracoContext);
foreach (var content in args.PublishedEntities)
{
var publishedContent = helper.TypedContent(content.Id);
var url = publishedContent.Url;
// do something with that url
...
}
}
};
}
}
@Shazwazza
Copy link

NO NO NO! On line 16 https://gist.github.com/sniffdk/7600822#file-contexthelper-cs-L16

NEVER do that, you don't need to set the HttpContext.Current to something, there's a reason it is null, it is not in a web context. If you want an UmbracoContext outside of a request, you can create your own http context (standalone) and just pass it in. Never set the HttpContext.Current, bad things will happen because many threads rely on the check to see if it exists to determine if it is a web request and that is how it manages request lifespans for certain objects.

Unfortunately Lee has copied your code and is causing us some very nasty issues with Courier and NH sessions.

There's only one reason to ever set HttpContext.Current is when you are changing threads manually and want to set the new threads HttpContext singleton to the already existing - and even that generally speaking is frowned upon.

@sniffdk
Copy link
Author

sniffdk commented Aug 4, 2015

Hi Shannon

Just realized you commented here, thanks :)
I understand what you are saying, but how would you handle situations where e.g. Ditto uses Autofac to resolve something and Autofac at some point goes:
if (HttpContext.Current == null)
{
throw new InvalidOperationException(RequestLifetimeScopeProviderResources.HttpContextNotAvailable);
}

@Shazwazza
Copy link

Autofac happily registers callbacks as per request lifetime, i can't remember the exact syntax but all DI containers will do something like:

container.Register(x => UmbracoContext.Current).AsRequestLifetime();

However, in the case where there is no request context you cannot inject a non-request instance when there is no request.

If you wanted to inject a custom UmbracoContext to a service where no request is available, then you'd need to create a transient factory with a custom object like:

public class TransientUmbracoContextFactory  {
    public UmbracoContext GetUmbracoContext() {
       //do stuff to create a custom umb context with a fake http context... DO NOT set HttpContext.Current
    }
}

Then in a non-request service you could have TransientUmbracoContextFactory injected

If you really need an UmbracoContext outside of the web you can create a fake http context... BUT THERE IS ZERO REASON to set HttpContext.Current, you just use your stand-alone instance.

@YodasMyDad
Copy link

@sniffdk I just changed the class to this

public static class ContextHelper
{
    public static void EnsureUmbracoContext()
    {
        if (UmbracoContext.Current == null)
        {
            var dummyHttpContext = new HttpContextWrapper(new HttpContext(new SimpleWorkerRequest("blah.aspx", "", new StringWriter())));
            UmbracoContext.EnsureContext(dummyHttpContext,ApplicationContext.Current,new WebSecurity(dummyHttpContext, ApplicationContext.Current), false);
        }
    }
}

And used it like this

        // As this is a task running outside the HttpContext, we need to ensure
        // we have the UmbracoContext
        ContextHelper.EnsureUmbracoContext();

        // Now we can carry on as normal and use the UmbracoContext
        var websites = UmbracoContext.Current.ContentCache.GetByXPath("//Website");

And it seems to work fine. Hopefully this is ok @Shazwazza

@TorbenRahbekKoch
Copy link

@leen3o Be aware that that specific EnsureContext overload is obsoleted (at least in 7.3).

@YodasMyDad
Copy link

Just found that out!! @Shazwazza how do we do it in v7.3?

@YodasMyDad
Copy link

Figured it out

        if (UmbracoContext.Current == null)
        {
            var dummyHttpContext = new HttpContextWrapper(new HttpContext(new SimpleWorkerRequest("blah.aspx", "", new StringWriter())));
            UmbracoContext.EnsureContext(
                dummyHttpContext,
                ApplicationContext.Current,
                new WebSecurity(dummyHttpContext, ApplicationContext.Current),
                UmbracoConfig.For.UmbracoSettings(),
                UrlProviderResolver.Current.Providers,
                false);
        }

@sniffdk
Copy link
Author

sniffdk commented Sep 30, 2015

Thanks all for chipping in, finally got around to update the gist, #h5yr ! 😃

@Shazwazza
Copy link

Hi, Everyone... please don't use singletons when you don't have to

  • EnsureContext returns the instance of UmbracoContext that it creates ... use it. Don't use UmbracoContext.Current if you already have an instance of the object you want. This same concept is true for 95% of things you do in Umbraco - we expose all context's as properties on all base classes... If you don't absolutely require access to singletons, don't use them.
  • @sniffdk - in your code you are creating a new UmbracoHelper inside of your loop... this is extra overhead for no reason, you only need one UmbracoHelper

@sniffdk
Copy link
Author

sniffdk commented Oct 7, 2015

Hi @Shazwazza

Again, thanks for chipping in. I've updated the code once more.

Cheers
Mads

@CamillePhosphor
Copy link

When trying the above code fragment as posted by leen3o null reference exceptions are occurring with Umbraco.Web.NotificationServiceExtensions.SendNotification(INotificationService service, IUmbracoEntity entity, IAction action, UmbracoContext umbracoContext, ApplicationContext applicationContext) during content/media service publish/save calls (remote updates and posts). Is there any chance of alternatives as the ensure context method does not complete the ensuring of context availability for service calls, or as in the example by leen3o will not use the iis virtual directory for media file storage (whereas a normal call within a page request would). Ideally the ability to start threads/tasks to complete in the background without slowing down a page request is desired. I.E. user submits changes to a property with the follow on effects of node & subtree generation & without the user having to wait for the subtree to be generated they would be able to nav to another area. the subtree can be queued to be added in the background later to be referenced by views etc. The creation should also be using the same media folder path as configured.

@CamillePhosphor
Copy link

Decided against trying to rely on ensure context etc to run code in separate threaded tasks but rather to create & send additional async get/post httpwebrequests with data attached. This also resolved the weird issue of the media save path mismatching the media retrieve path (one being physical and the other virtual). However on server startup actions etc would still experience some issues with ensure context with further null pointer calls inside Umbraco services. The following thread helped https://our.umbraco.org/forum/developers/api-questions/54671-UmbracoContext-in-New-Thread-to-Populate-Cache-Asynchronously It is likely I will try to get the server to run any initialization checks & presetup using async httpwebrequests as well.

@tajamal
Copy link

tajamal commented Jan 28, 2016

EnsureContext is deprecated, what do i instead? i am new to umbraco, can you help.

@sniffdk
Copy link
Author

sniffdk commented May 3, 2016

@tajamal sorry about the late answer, EnsureContext is not deprecated, just most of the overloads.

@jbreuer
Copy link

jbreuer commented Jun 24, 2016

I'm using this code to ensure the UmbracoContext.Current in the ApplicationStarting event. That way I can create an UmbracoHelper and use TypedContent because I need to fetch some nodes. Is this the best way to do this?

@alindgren
Copy link

@jbreuer, I think you can pass UmbracoHelper to the event handler. For example,

protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
  var helper = new UmbracoHelper(UmbracoContext.Current);
  ExamineManager.Instance.IndexProviderCollection["ExternalIndexer"].GatheringNodeData 
        += (sender, e) =>   Indexer_GatheringNodeData(sender, e, helper);
}

I saw @JimBobSquarePants was getting UmbracoHelper as a param in the event handler in this article: http://24days.in/umbraco/2015/hacking-around-with-search-and-strong-typed-models/. And I saw how this was done in this article: http://staheri.com/my-blog/2015/march/custom-examine-indexing-using-umbraco-cache/.

This works for me (able to use TypedContent()) as follows:

private void Indexer_GatheringNodeData(object sender, IndexingNodeDataEventArgs e, UmbracoHelper helper)
{
  var node = helper.TypedContent(e.NodeId);
}

However, I was getting an exception when using Vorto (when calling GetVortoValue() on the IPublishedContent node). The technique in this gist seems to remedy that. Thanks @sniffdk and @Shazwazza!

@pgriffithsamaze
Copy link

pgriffithsamaze commented Nov 16, 2016

Hi Guys,

I'm really struggling with this to. Its all new so i haven't got a great understanding sorry.

I have a custom class where I am trying to use the UmbracoHelper.TypedContentAtRoot() to dynamically get the root node but the UmbracoContext.Current is always null and therefore it returns nothing. I have tried using the code above suggested by @YodasMyDad but it doesn't seem to work

if (UmbracoContext.Current == null) { var dummyHttpContext = new HttpContextWrapper(new HttpContext(new SimpleWorkerRequest("blah.aspx", "", new StringWriter()))); UmbracoContext.EnsureContext( dummyHttpContext, ApplicationContext.Current, new WebSecurity(dummyHttpContext, ApplicationContext.Current), UmbracoConfig.For.UmbracoSettings(), UrlProviderResolver.Current.Providers, false); }

If i use the following it seems to work. However, through my lack of understanding and resharpher warning me that EnsureContext is obsolete it concerns me of the potential impact.

var dummyContext = HttpContext.Current = new HttpContext(new HttpRequest(null, "https://www.google.com", null), new HttpResponse(null)); UmbracoContext.EnsureContext(new HttpContextWrapper(dummyContext), ApplicationContext.Current);

Reading here https://our.umbraco.org/forum/extending-umbraco-and-using-the-api/76889-background-process-value-cannot-be-nullparameter-name-httpcontext i can see that its a bug in 7.4.3. I am using 7.5.3 assembly: 1.0.6092.24019. I have also raised this in OUR https://our.umbraco.org/forum/extending-umbraco-and-using-the-api//81341-contentservice-not-creating-content. This will show you my code and what im trying to do. In short i am trying to do the following in a custom class

  1. Dynamically get the root node
  2. Get the first child where the document type == "reviewPage"
  3. Create child nodes of the reviewPage and setting values using the CreateContent & SaveAndPublishWithStatus.

Can some one please help?

Thanks
Paul

@bipin24x7
Copy link

Team, Do we have final confirmed new method which works with Umbraco 7.5.10? EnsureContext is deprecated. So, if we have any other option to generate UmbracoContext?

@sniffdk
Copy link
Author

sniffdk commented Mar 31, 2017

EnsureContext is not deprecated, not when looking at the source at least -> https://github.com/umbraco/Umbraco-CMS/blob/release-7.5.11/src/Umbraco.Web/UmbracoContext.cs#L113
If anyone needs help on this, come find us in the unofficial Slack channel -> umbracians.slack.com #help

@MichaelaIvanova
Copy link

Hi @Shazwazza,

Is is OK to have set up like this using Autofac:

 builder.Register(c => UmbracoContext.Current).InstancePerRequest();
 builder.Register(x => new UmbracoHelper(UmbracoContext.Current)).InstancePerRequest();

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment