diff --git a/src/Umbraco.Core/Events/ScopeLifespanMessagesFactory.cs b/src/Umbraco.Core/Events/ScopeLifespanMessagesFactory.cs
new file mode 100644
index 0000000000..710ee8a129
--- /dev/null
+++ b/src/Umbraco.Core/Events/ScopeLifespanMessagesFactory.cs
@@ -0,0 +1,58 @@
+using System;
+using Umbraco.Core.Scoping;
+
+namespace Umbraco.Core.Events
+{
+ ///
+ /// Stores the instance of EventMessages in the current scope.
+ ///
+ internal class ScopeLifespanMessagesFactory : IEventMessagesFactory
+ {
+ public const string ContextKey = "Umbraco.Core.Events.ScopeLifespanMessagesFactory";
+
+ private readonly IHttpContextAccessor _contextAccessor;
+ private readonly IScopeProviderInternal _scopeProvider;
+
+ public static ScopeLifespanMessagesFactory Current { get; private set; }
+
+ public ScopeLifespanMessagesFactory(IHttpContextAccessor contextAccesor, IScopeProvider scopeProvider)
+ {
+ if (contextAccesor == null) throw new ArgumentNullException("contextAccesor");
+ if (scopeProvider == null) throw new ArgumentNullException("scopeProvider");
+ if (scopeProvider is IScopeProviderInternal == false) throw new ArgumentException("Not IScopeProviderInternal.", "scopeProvider");
+ _contextAccessor = contextAccesor;
+ _scopeProvider = (IScopeProviderInternal) scopeProvider;
+ Current = this;
+ }
+
+ public EventMessages Get()
+ {
+ var messages = GetFromHttpContext();
+ if (messages != null) return messages;
+
+ var scope = _scopeProvider.GetAmbientOrNoScope();
+ return scope.Messages;
+ }
+
+ public EventMessages GetFromHttpContext()
+ {
+ if (_contextAccessor == null || _contextAccessor.Value == null) return null;
+ return (EventMessages)_contextAccessor.Value.Items[ContextKey];
+ }
+
+ public EventMessages TryGet()
+ {
+ var messages = GetFromHttpContext();
+ if (messages != null) return messages;
+
+ var scope = _scopeProvider.AmbientScope;
+ return scope == null ? null : scope.MessagesOrNull;
+ }
+
+ public void Set(EventMessages messages)
+ {
+ if (_contextAccessor.Value == null) return;
+ _contextAccessor.Value.Items[ContextKey] = messages;
+ }
+ }
+}
diff --git a/src/Umbraco.Core/IHttpContextAccessor.cs b/src/Umbraco.Core/IHttpContextAccessor.cs
new file mode 100644
index 0000000000..a0873c78a9
--- /dev/null
+++ b/src/Umbraco.Core/IHttpContextAccessor.cs
@@ -0,0 +1,9 @@
+using System.Web;
+
+namespace Umbraco.Core
+{
+ public interface IHttpContextAccessor
+ {
+ HttpContextBase Value { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs
index 2d8fa245b0..a21815173c 100644
--- a/src/Umbraco.Core/Scoping/NoScope.cs
+++ b/src/Umbraco.Core/Scoping/NoScope.cs
@@ -15,6 +15,7 @@ namespace Umbraco.Core.Scoping
private bool _disposed;
private UmbracoDatabase _database;
+ private EventMessages _messages;
public NoScope(ScopeProvider scopeProvider)
{
@@ -61,12 +62,42 @@ namespace Umbraco.Core.Scoping
///
public EventMessages Messages
{
- get { throw new NotSupportedException(); }
+ get
+ {
+ EnsureNotDisposed();
+ if (_messages != null) return _messages;
+
+ // see comments in Scope
+
+ var factory = ScopeLifespanMessagesFactory.Current;
+ if (factory == null)
+ {
+ _messages = new EventMessages();
+ }
+ else
+ {
+ _messages = factory.GetFromHttpContext();
+ if (_messages == null)
+ factory.Set(_messages = new EventMessages());
+ }
+
+ return _messages;
+ }
}
public EventMessages MessagesOrNull
{
- get { throw new NotSupportedException(); }
+ get
+ {
+ EnsureNotDisposed();
+
+ // see comments in Scope
+
+ if (_messages != null) return _messages;
+
+ var factory = ScopeLifespanMessagesFactory.Current;
+ return factory == null ? null : factory.GetFromHttpContext();
+ }
}
///
diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs
index ab66be1bd2..53d35181c3 100644
--- a/src/Umbraco.Core/Scoping/Scope.cs
+++ b/src/Umbraco.Core/Scoping/Scope.cs
@@ -24,6 +24,7 @@ namespace Umbraco.Core.Scoping
private IsolatedRuntimeCache _isolatedRuntimeCache;
private UmbracoDatabase _database;
+ private EventMessages _messages;
private ICompletable _fscope;
private IEventDispatcher _eventDispatcher;
@@ -127,6 +128,7 @@ namespace Umbraco.Core.Scoping
{
// steal everything from NoScope
_database = noScope.DatabaseOrNull;
+ _messages = noScope.MessagesOrNull;
// make sure the NoScope can be replaced ie not in a transaction
if (_database != null && _database.InTransaction)
@@ -271,11 +273,27 @@ namespace Umbraco.Core.Scoping
{
EnsureNotDisposed();
if (ParentScope != null) return ParentScope.Messages;
- //return _messages ?? (_messages = new EventMessages());
- // ok, this isn't pretty, but it works
- // TODO kill the message factory and let the scope manage it all
- return ApplicationContext.Current.Services.EventMessagesFactory.Get();
+ if (_messages != null) return _messages;
+
+ // this is ugly - in v7 for backward compatibility reasons, EventMessages need
+ // to survive way longer that the scopes, and kinda resides on its own in http
+ // context, but must also be in scopes for when we do async and lose http context
+ // TODO refactor in v8
+
+ var factory = ScopeLifespanMessagesFactory.Current;
+ if (factory == null)
+ {
+ _messages = new EventMessages();
+ }
+ else
+ {
+ _messages = factory.GetFromHttpContext();
+ if (_messages == null)
+ factory.Set(_messages = new EventMessages());
+ }
+
+ return _messages;
}
}
@@ -284,7 +302,14 @@ namespace Umbraco.Core.Scoping
get
{
EnsureNotDisposed();
- return ParentScope == null ? null : ParentScope.MessagesOrNull;
+ if (ParentScope != null) return ParentScope.MessagesOrNull;
+
+ // see comments in Messages
+
+ if (_messages != null) return _messages;
+
+ var factory = ScopeLifespanMessagesFactory.Current;
+ return factory == null ? null : factory.GetFromHttpContext();
}
}
diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs
index 3ff28aee0a..b414d381e9 100644
--- a/src/Umbraco.Core/Services/EntityService.cs
+++ b/src/Umbraco.Core/Services/EntityService.cs
@@ -91,6 +91,7 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.Member:
case UmbracoObjectTypes.DataType:
case UmbracoObjectTypes.DocumentTypeContainer:
+ uow.Commit();
return uow.Database.ExecuteScalar(new Sql().Select("id").From().Where(dto => dto.UniqueId == key));
case UmbracoObjectTypes.RecycleBin:
case UmbracoObjectTypes.Stylesheet:
@@ -129,6 +130,7 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.DocumentType:
case UmbracoObjectTypes.Member:
case UmbracoObjectTypes.DataType:
+ uow.Commit();
return uow.Database.ExecuteScalar(new Sql().Select("uniqueID").From().Where(dto => dto.NodeId == id));
case UmbracoObjectTypes.RecycleBin:
case UmbracoObjectTypes.Stylesheet:
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 6f02366820..8726469f53 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -315,6 +315,8 @@
+
+
diff --git a/src/Umbraco.Web/IHttpContextAccessor.cs b/src/Umbraco.Web/IHttpContextAccessor.cs
index 068783725a..4b5a8ed884 100644
--- a/src/Umbraco.Web/IHttpContextAccessor.cs
+++ b/src/Umbraco.Web/IHttpContextAccessor.cs
@@ -1,5 +1,3 @@
-using System.Web;
-
namespace Umbraco.Web
{
///
@@ -8,8 +6,6 @@ namespace Umbraco.Web
///
/// NOTE: This has a singleton lifespan
///
- public interface IHttpContextAccessor
- {
- HttpContextBase Value { get; }
- }
+ public interface IHttpContextAccessor : Core.IHttpContextAccessor
+ { }
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/RequestLifespanMessagesFactory.cs b/src/Umbraco.Web/RequestLifespanMessagesFactory.cs
deleted file mode 100644
index cf75e60755..0000000000
--- a/src/Umbraco.Web/RequestLifespanMessagesFactory.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System;
-using System.Runtime.Remoting.Messaging;
-using Umbraco.Core.Events;
-
-namespace Umbraco.Web
-{
- ///
- /// Stores the instance of EventMessages in the current request so all events will share the same instance
- ///
- internal class RequestLifespanMessagesFactory : IEventMessagesFactory
- {
- private const string ContextKey = "Umbraco.Web.RequestLifespanMessagesFactory";
- private readonly IHttpContextAccessor _httpAccessor;
-
- public RequestLifespanMessagesFactory(IHttpContextAccessor httpAccessor)
- {
- if (httpAccessor == null) throw new ArgumentNullException("httpAccessor");
- _httpAccessor = httpAccessor;
- }
-
- public EventMessages Get()
- {
- var httpContext = _httpAccessor.Value;
- if (httpContext != null)
- {
- var eventMessages = httpContext.Items[ContextKey] as EventMessages;
- if (eventMessages == null) httpContext.Items[ContextKey] = eventMessages = new EventMessages();
- return eventMessages;
- }
-
- var lccContext = CallContext.LogicalGetData(ContextKey) as EventMessages;
- if (lccContext != null) return lccContext;
-
- throw new Exception("Could not get messages.");
- }
-
- public EventMessages TryGet()
- {
- var httpContext = _httpAccessor.Value;
- return httpContext != null
- ? httpContext.Items[ContextKey] as EventMessages
- : CallContext.LogicalGetData(ContextKey) as EventMessages;
- }
-
- // Deploy wants to execute things outside of a request, where this factory would fail,
- // so the factory is extended so that Deploy can Set/Clear event messages in the logical
- // call context (which flows with async) - it needs to be set and cleared because, contrary
- // to http context, it's not being cleared at the end of anything.
- //
- // to be refactored in v8! the whole IEventMessagesFactory is borked anyways
-
- public void SetLlc()
- {
- CallContext.LogicalSetData(ContextKey, new EventMessages());
- }
-
- public void ClearLlc()
- {
- CallContext.FreeNamedDataSlot(ContextKey);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index f3f03d2b83..7987a46b3b 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -374,7 +374,6 @@
-
diff --git a/src/Umbraco.Web/UmbracoContextExtensions.cs b/src/Umbraco.Web/UmbracoContextExtensions.cs
index 53ef92690e..3e5c9d355e 100644
--- a/src/Umbraco.Web/UmbracoContextExtensions.cs
+++ b/src/Umbraco.Web/UmbracoContextExtensions.cs
@@ -53,7 +53,7 @@ namespace Umbraco.Web
///
public static EventMessages GetCurrentEventMessages(this UmbracoContext umbracoContext)
{
- var eventMessagesFactory = umbracoContext.Application.Services.EventMessagesFactory as RequestLifespanMessagesFactory;
+ var eventMessagesFactory = umbracoContext.Application.Services.EventMessagesFactory as ScopeLifespanMessagesFactory;
return eventMessagesFactory == null ? null : eventMessagesFactory.TryGet();
}
diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs
index b0ac28dfe5..8b935b1ed6 100644
--- a/src/Umbraco.Web/WebBootManager.cs
+++ b/src/Umbraco.Web/WebBootManager.cs
@@ -42,6 +42,7 @@ using Umbraco.Web.UI.JavaScript;
using Umbraco.Web.WebApi;
using umbraco.BusinessLogic;
using Umbraco.Core.Cache;
+using Umbraco.Core.Events;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Publishing;
@@ -92,7 +93,7 @@ namespace Umbraco.Web
protected override ServiceContext CreateServiceContext(DatabaseContext dbContext, IScopeProvider scopeProvider)
{
//use a request based messaging factory
- var evtMsgs = new RequestLifespanMessagesFactory(new SingletonHttpContextAccessor());
+ var evtMsgs = new ScopeLifespanMessagesFactory(new SingletonHttpContextAccessor(), scopeProvider);
return new ServiceContext(
new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()),
new PetaPocoUnitOfWorkProvider(scopeProvider),