Scope - fix issue with EventMessages
This commit is contained in:
58
src/Umbraco.Core/Events/ScopeLifespanMessagesFactory.cs
Normal file
58
src/Umbraco.Core/Events/ScopeLifespanMessagesFactory.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the instance of EventMessages in the current scope.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/Umbraco.Core/IHttpContextAccessor.cs
Normal file
9
src/Umbraco.Core/IHttpContextAccessor.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public interface IHttpContextAccessor
|
||||
{
|
||||
HttpContextBase Value { get; }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ namespace Umbraco.Core.Services
|
||||
case UmbracoObjectTypes.Member:
|
||||
case UmbracoObjectTypes.DataType:
|
||||
case UmbracoObjectTypes.DocumentTypeContainer:
|
||||
uow.Commit();
|
||||
return uow.Database.ExecuteScalar<int?>(new Sql().Select("id").From<NodeDto>().Where<NodeDto>(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<Guid?>(new Sql().Select("uniqueID").From<NodeDto>().Where<NodeDto>(dto => dto.NodeId == id));
|
||||
case UmbracoObjectTypes.RecycleBin:
|
||||
case UmbracoObjectTypes.Stylesheet:
|
||||
|
||||
@@ -315,6 +315,8 @@
|
||||
<Compile Include="Events\EventDefinitionFilter.cs" />
|
||||
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
|
||||
<Compile Include="Events\ScopeEventDispatcherBase.cs" />
|
||||
<Compile Include="Events\ScopeLifespanMessagesFactory.cs" />
|
||||
<Compile Include="IHttpContextAccessor.cs" />
|
||||
<Compile Include="OrderedHashSet.cs" />
|
||||
<Compile Include="Events\UninstallPackageEventArgs.cs" />
|
||||
<Compile Include="Models\GridValue.cs" />
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
/// <summary>
|
||||
@@ -8,8 +6,6 @@ namespace Umbraco.Web
|
||||
/// <remarks>
|
||||
/// NOTE: This has a singleton lifespan
|
||||
/// </remarks>
|
||||
public interface IHttpContextAccessor
|
||||
{
|
||||
HttpContextBase Value { get; }
|
||||
}
|
||||
public interface IHttpContextAccessor : Core.IHttpContextAccessor
|
||||
{ }
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using Umbraco.Core.Events;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the instance of EventMessages in the current request so all events will share the same instance
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,7 +374,6 @@
|
||||
<Compile Include="Routing\RedirectTrackingEventHandler.cs" />
|
||||
<Compile Include="Editors\RedirectUrlManagementController.cs" />
|
||||
<Compile Include="Models\ContentEditing\RedirectUrlSearchResults.cs" />
|
||||
<Compile Include="RequestLifespanMessagesFactory.cs" />
|
||||
<Compile Include="RouteDataExtensions.cs" />
|
||||
<Compile Include="Routing\ContentFinderByRedirectUrl.cs" />
|
||||
<Compile Include="Scheduling\LatchedBackgroundTaskBase.cs" />
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Umbraco.Web
|
||||
/// <returns></returns>
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user