Skip to content
This repository has been archived by the owner on Dec 13, 2021. It is now read-only.

Commit

Permalink
Rewrite of use of UmbracoContext & ApplicationContext (See description)
Browse files Browse the repository at this point in the history
Based on the idea of https://github.com/umbraco/Umbraco-CMS/blob/release-7.3.2/src/Umbraco.Web/IUmbracoContextAccessor.cs & https://github.com/umbraco/Umbraco-CMS/blob/release-7.3.2/src/Umbraco.Web/SingletonUmbracoContextAccessor.cs

I've implemented an "UmbracoApplicationContextAccessor which allows readonly access to the UmbracoContext & ApplicationContext. In most cases this will use the SingletonUmbracoApplicationContextAccessor but in the case of tests there is a publicly available static register method to setup an alternative which used a mocked version of this accessor.

Not happy about the accessor being passed through so many properties but it's the best way I can think of to ensure we're using exactly the same contexts from method to method.
  • Loading branch information
jamiepollock committed Jan 22, 2017
1 parent e25d132 commit bd4c79a
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 32 deletions.
40 changes: 40 additions & 0 deletions src/Our.Umbraco.Ditto/Common/UmbracoApplicationContextAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Umbraco.Core;
using Umbraco.Web;

namespace Our.Umbraco.Ditto
{
/// <summary>
/// An interface allowing access to the UmbracoContext &amp; ApplicationContext without resulting to using the singletons directly.
/// </summary>
public interface IUmbracoApplicationContextAccessor
{
/// <summary>
/// The UmbracoContext instance.
/// </summary>
UmbracoContext UmbracoContext { get; }

/// <summary>
/// The ApplicationContext instance.
/// </summary>
ApplicationContext ApplicationContext { get; }
}

internal class SingletonUmbracoApplicationContextAccessor : IUmbracoApplicationContextAccessor
{
public UmbracoContext UmbracoContext
{
get
{
return UmbracoContext.Current;
}
}

public ApplicationContext ApplicationContext
{
get
{
return ApplicationContext.Current;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Our.Umbraco.Ditto
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,13 @@ public abstract class DittoProcessorAttribute : DittoCacheableAttribute
/// <summary>
/// Initializes a new instance of the <see cref="DittoProcessorAttribute"/> class.
/// </summary>
protected DittoProcessorAttribute() : this(UmbracoContext.Current, ApplicationContext.Current) { }

/// <summary>
/// Initializes a new instance of the <see cref="DittoProcessorAttribute"/> class.
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="applicationContext"></param>
protected DittoProcessorAttribute(UmbracoContext umbracoContext, ApplicationContext applicationContext)
protected DittoProcessorAttribute()
{
if (umbracoContext == null)
{
throw new ArgumentNullException("umbracoContext");
}

if (applicationContext == null)
{
throw new ArgumentNullException("applicationContext");
}
var metaData = this.GetType().GetCustomAttribute<DittoProcessorMetaDataAttribute>(true);
if (metaData == null)
{
throw new ApplicationException("Ditto processor attributes require a DittoProcessorMetaData attribute to be applied to the class but none was found");
}
this.Umbraco = umbracoContext;
this.ApplicationContext = applicationContext;

this.ValueType = metaData.ValueType;
this.ContextType = metaData.ContextType;
Expand Down Expand Up @@ -115,12 +97,12 @@ protected virtual ProfilingLogger ProfilingLogger
/// <summary>
/// Returns the current UmbracoContext
/// </summary>
protected virtual UmbracoContext Umbraco { get; private set; }
public virtual UmbracoContext Umbraco { get; internal set; }

/// <summary>
/// Returns the current ApplicationContext
/// </summary>
protected virtual ApplicationContext ApplicationContext { get; private set; }
public virtual ApplicationContext ApplicationContext { get; internal set; }

/// <summary>
/// Returns a ServiceContext
Expand Down Expand Up @@ -155,7 +137,7 @@ protected DatabaseContext DatabaseContext
/// The <see cref="object" /> representing the processed value.
/// </returns>
internal virtual object ProcessValue(
object value,
object value,
DittoProcessorContext context)
{
if (value != null && !this.ValueType.IsInstanceOfType(value))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ internal class DittoProcessorRegistry
/// </summary>
private Type defaultProcessorType = typeof(UmbracoPropertyAttribute);

/// <summary>
/// The default umbraco application context accessor type for processors, (defaults to `SingletonUmbracoApplicationContextAccessor`).
/// </summary>
private Type defaultUmbracoApplicationContextAccessorType = typeof(SingletonUmbracoApplicationContextAccessor);

/// <summary>
/// Prevents a default instance of the <see cref="DittoProcessorRegistry"/> class from being created.
/// </summary>
Expand Down Expand Up @@ -143,5 +148,26 @@ public IEnumerable<DittoProcessorAttribute> GetRegisteredProcessorAttributesFor(
: Enumerable.Empty<DittoProcessorAttribute>();
}
}

/// <summary>
/// Registers the default umbraco application context accessor type.
/// </summary>
/// <typeparam name="TUmbracoApplicationContextAccessorType">The umbraco application context accessor type.</typeparam>
public void RegisterDefaultUmbracoApplicationContextAccessorType<TUmbracoApplicationContextAccessorType>()
where TUmbracoApplicationContextAccessorType : IUmbracoApplicationContextAccessor, new()
{
this.defaultUmbracoApplicationContextAccessorType = typeof(TUmbracoApplicationContextAccessorType);
}

/// <summary>
/// Gets the default umbraco application context accessor type.
/// </summary>
/// <returns>
/// Returns the default umbraco application context accessor type.
/// </returns>
public Type GetDefaultUmbracoApplicationContextAccessorType()
{
return this.defaultUmbracoApplicationContextAccessorType;
}
}
}
10 changes: 10 additions & 0 deletions src/Our.Umbraco.Ditto/Ditto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,15 @@ public static void RegisterDefaultProcessorType<TProcessorAttributeType>()
{
TypeDescriptor.AddAttributes(typeof(TObjectType), new TypeConverterAttribute(typeof(TConverterType)));
}

/// <summary>
/// Registers a global umbraco application context accessor.
/// </summary>
/// <typeparam name="TUmbracoApplicationContextAccessorType">The type of the accessor.</typeparam>
public static void RegisterUmbracoApplicationContextAccessor<TUmbracoApplicationContextAccessorType>()
where TUmbracoApplicationContextAccessorType : IUmbracoApplicationContextAccessor, new()
{
DittoProcessorRegistry.Instance.RegisterDefaultUmbracoApplicationContextAccessorType<TUmbracoApplicationContextAccessorType>();
}
}
}
34 changes: 25 additions & 9 deletions src/Our.Umbraco.Ditto/Extensions/PublishedContentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Our.Umbraco.Ditto
{
using global::Umbraco.Core;
using System.Collections;

/// <summary>
Expand Down Expand Up @@ -192,12 +193,15 @@ public static class PublishedContentExtensions
throw new ArgumentException(string.Format("The instance parameter does not implement Type '{0}'", type.Name), "instance");
}

// Get the accessor for UmbracoContext & ApplicationContext
var defaultUmbracoApplicationContextAccessor = (IUmbracoApplicationContextAccessor)Activator.CreateInstance(DittoProcessorRegistry.Instance.GetDefaultUmbracoApplicationContextAccessorType());

// Check if the culture has been set, otherwise use from Umbraco, or fallback to a default
if (culture == null)
{
if (UmbracoContext.Current != null && UmbracoContext.Current.PublishedContentRequest != null)
if (defaultUmbracoApplicationContextAccessor.UmbracoContext != null && defaultUmbracoApplicationContextAccessor.UmbracoContext.PublishedContentRequest != null)
{
culture = UmbracoContext.Current.PublishedContentRequest.Culture;
culture = defaultUmbracoApplicationContextAccessor.UmbracoContext.PublishedContentRequest.Culture;
}
else
{
Expand All @@ -213,11 +217,11 @@ public static class PublishedContentExtensions
if (cacheAttr != null)
{
var ctx = new DittoCacheContext(cacheAttr, content, type, culture);
return cacheAttr.GetCacheItem(ctx, () => ConvertContent(content, type, culture, instance, processorContexts, onConverting, onConverted));
return cacheAttr.GetCacheItem(ctx, () => ConvertContent(content, type, defaultUmbracoApplicationContextAccessor, culture, instance, processorContexts, onConverting, onConverted));
}
else
{
return ConvertContent(content, type, culture, instance, processorContexts, onConverting, onConverted);
return ConvertContent(content, type, defaultUmbracoApplicationContextAccessor, culture, instance, processorContexts, onConverting, onConverted);
}
}
}
Expand All @@ -231,6 +235,9 @@ public static class PublishedContentExtensions
/// <param name="type">
/// The <see cref="Type"/> of items to return.
/// </param>
/// <param name="umbracoApplicationContextAccessor">
/// The umbraco application context accessor.
/// </param>
/// <param name="culture">
/// The <see cref="CultureInfo"/>
/// </param>
Expand All @@ -255,6 +262,7 @@ public static class PublishedContentExtensions
private static object ConvertContent(
IPublishedContent content,
Type type,
IUmbracoApplicationContextAccessor umbracoApplicationContextAccessor,
CultureInfo culture = null,
object instance = null,
IEnumerable<DittoProcessorContext> processorContexts = null,
Expand Down Expand Up @@ -334,7 +342,7 @@ public static class PublishedContentExtensions
var localInstance = instance;

// ReSharper disable once PossibleMultipleEnumeration
lazyProperties.Add(propertyInfo.Name, new Lazy<object>(() => GetProcessedValue(content, culture, type, deferredPropertyInfo, localInstance, defaultProcessorType, processorContexts)));
lazyProperties.Add(propertyInfo.Name, new Lazy<object>(() => GetProcessedValue(content, culture, type, deferredPropertyInfo, localInstance, defaultProcessorType, umbracoApplicationContextAccessor, processorContexts)));
}
}

Expand Down Expand Up @@ -367,7 +375,7 @@ public static class PublishedContentExtensions

// Set the value normally.
// ReSharper disable once PossibleMultipleEnumeration
var value = GetProcessedValue(content, culture, type, propertyInfo, instance, defaultProcessorType, processorContexts);
var value = GetProcessedValue(content, culture, type, propertyInfo, instance, defaultProcessorType, umbracoApplicationContextAccessor, processorContexts);

// This is 2x as fast as propertyInfo.SetValue(instance, value, null);
PropertyInfoInvocations.SetValue(propertyInfo, instance, value);
Expand All @@ -390,6 +398,7 @@ public static class PublishedContentExtensions
/// <param name="propertyInfo">The <see cref="PropertyInfo" /> property info associated with the type.</param>
/// <param name="instance">The instance to assign the value to.</param>
/// <param name="defaultProcessorType">The default processor type.</param>
/// <param name="umbracoApplicationContextAccessor">The umbraco application context accessor.</param>
/// <param name="processorContexts">A collection of <see cref="DittoProcessorContext" /> entities to use whilst processing values.</param>
/// <returns>
/// The <see cref="object" /> representing the Umbraco value.
Expand All @@ -401,6 +410,7 @@ public static class PublishedContentExtensions
PropertyInfo propertyInfo,
object instance,
Type defaultProcessorType,
IUmbracoApplicationContextAccessor umbracoApplicationContextAccessor,
IEnumerable<DittoProcessorContext> processorContexts = null)
{
// Time custom value-processor.
Expand All @@ -414,11 +424,11 @@ public static class PublishedContentExtensions
if (cacheAttr != null)
{
var ctx = new DittoCacheContext(cacheAttr, content, targetType, propertyDescriptor, culture);
return cacheAttr.GetCacheItem(ctx, () => DoGetProcessedValue(content, culture, targetType, propertyInfo, propertyDescriptor, defaultProcessorType, processorContexts));
return cacheAttr.GetCacheItem(ctx, () => DoGetProcessedValue(content, culture, targetType, propertyInfo, propertyDescriptor, defaultProcessorType, umbracoApplicationContextAccessor, processorContexts));
}
else
{
return DoGetProcessedValue(content, culture, targetType, propertyInfo, propertyDescriptor, defaultProcessorType, processorContexts);
return DoGetProcessedValue(content, culture, targetType, propertyInfo, propertyDescriptor, defaultProcessorType, umbracoApplicationContextAccessor, processorContexts);
}
}
}
Expand All @@ -432,6 +442,7 @@ public static class PublishedContentExtensions
/// <param name="propertyInfo">The property information.</param>
/// <param name="propertyDescriptor">The property descriptor.</param>
/// <param name="defaultProcessorType">The default processor type.</param>
/// <param name="umbracoApplicationContextAccessor">The umbraco application context accessor.</param>
/// <param name="processorContexts">The processor contexts.</param>
/// <returns>Returns the processed value.</returns>
private static object DoGetProcessedValue(
Expand All @@ -441,6 +452,7 @@ public static class PublishedContentExtensions
PropertyInfo propertyInfo,
PropertyDescriptor propertyDescriptor,
Type defaultProcessorType,
IUmbracoApplicationContextAccessor umbracoApplicationContextAccessor,
IEnumerable<DittoProcessorContext> processorContexts = null)
{
// Check the property for any explicit processor attributes
Expand Down Expand Up @@ -492,13 +504,17 @@ public static class PublishedContentExtensions

// Add the passed in contexts
processorContextsCache.AddContexts(processorContexts);

// Process attributes
foreach (var processorAttr in processorAttrs)
{
// Get the right context type
var ctx = processorContextsCache.GetOrCreateContext(processorAttr.ContextType);

// Populate UmbracoContext & ApplicationContext
processorAttr.Umbraco = umbracoApplicationContextAccessor.UmbracoContext;
processorAttr.ApplicationContext = umbracoApplicationContextAccessor.ApplicationContext;

// Process value
currentValue = processorAttr.ProcessValue(currentValue, ctx);
}
Expand Down
1 change: 1 addition & 0 deletions src/Our.Umbraco.Ditto/Our.Umbraco.Ditto.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Common\CachedInvocations.cs" />
<Compile Include="Common\UmbracoApplicationContextAccessor.cs" />
<Compile Include="Common\PropertyInfoInvocations.cs" />
<Compile Include="ComponentModel\Attributes\DittoDefaultProcessorAttribute.cs" />
<Compile Include="ComponentModel\Attributes\DittoLazyAttribute.cs" />
Expand Down

0 comments on commit bd4c79a

Please sign in to comment.