-
-
Save sniffdk/7600822 to your computer and use it in GitHub Desktop.
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 | |
... | |
} | |
} | |
}; | |
} | |
} |
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);
}
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.
@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
@leen3o Be aware that that specific EnsureContext
overload is obsoleted (at least in 7.3).
Just found that out!! @Shazwazza how do we do it in v7.3?
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);
}
Thanks all for chipping in, finally got around to update the gist, #h5yr ! 😃
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
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.
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.
EnsureContext is deprecated, what do i instead? i am new to umbraco, can you help.
@tajamal sorry about the late answer, EnsureContext is not deprecated, just most of the overloads.
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?
@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!
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
- Dynamically get the root node
- Get the first child where the document type == "reviewPage"
- Create child nodes of the reviewPage and setting values using the CreateContent & SaveAndPublishWithStatus.
Can some one please help?
Thanks
Paul
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?
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
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!
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.