/// <summary>Returns the processed value for the given type and property.</summary> /// <param name="content">The <see cref="IPublishedContent" /> to convert.</param> /// <param name="culture">The <see cref="CultureInfo" /></param> /// <param name="config">The Ditto configuration for the type.</param> /// <param name="mappableProperty">Information about the mappable property.</param> /// <param name="instance">The instance to assign the value to.</param> /// <param name="chainContext">The <see cref="DittoChainContext"/> for the current processor chain.</param> /// <returns>The <see cref="object" /> representing the Umbraco value.</returns> private static object GetProcessedValue( IPublishedContent content, CultureInfo culture, DittoTypeInfo config, DittoTypeInfo.DittoTypePropertyInfo mappableProperty, object instance, DittoChainContext chainContext) { using (DittoDisposableTimer.DebugDuration(typeof(Ditto), $"Processing '{mappableProperty.PropertyInfo.Name}' ({content.Id})")) { // Create a base processor context for this current chain level var baseProcessorContext = new DittoProcessorContext { Content = content, TargetType = config.TargetType, PropertyInfo = mappableProperty.PropertyInfo, Culture = culture }; // Check for cache attribute if (mappableProperty.IsCacheable) { var ctx = new DittoCacheContext(mappableProperty.CacheInfo, content, config.TargetType, mappableProperty.PropertyInfo, culture); return(mappableProperty.CacheInfo.GetCacheItem(ctx, () => DoGetProcessedValue(content, mappableProperty, baseProcessorContext, chainContext))); } else { return(DoGetProcessedValue(content, mappableProperty, baseProcessorContext, chainContext)); } } }
/// <summary> /// Gets the cache item. /// </summary> /// <typeparam name="TOuputType">The type of the output type.</typeparam> /// <param name="cacheContext">The cache context.</param> /// <param name="refresher">The refresher.</param> /// <returns>Returns the output type.</returns> /// <exception cref="ApplicationException">Expected a cache key builder of type DittoProcessorCacheKeyBuilder but got CacheKeyBuilderType.</exception> internal TOuputType GetCacheItem <TOuputType>(DittoCacheContext cacheContext, Func <TOuputType> refresher) { // TODO: [LK:2018-01-18] Review this, does `cacheContext` need to be passed in? // Given that the values are available on the instance. // If no cache duration set, (and in debug mode AND NOT a unit-test), then just run the refresher if (this.CacheDuration == 0 || (Ditto.IsDebuggingEnabled && Ditto.IsRunningInUnitTest == false)) { return(refresher()); } // Get the cache key builder type var cacheKeyBuilderType = this.CacheKeyBuilderType ?? typeof(DittoDefaultCacheKeyBuilder); // Check the cache key builder type if (typeof(DittoCacheKeyBuilder).IsAssignableFrom(cacheKeyBuilderType) == false) { throw new ApplicationException($"Expected a cache key builder of type {typeof(DittoCacheKeyBuilder)} but got {cacheKeyBuilderType}"); } // TODO: [LK:2018-01-18] Review this, a new instance is being created per call // Construct the cache key builder var builder = cacheKeyBuilderType.GetInstance <DittoCacheKeyBuilder>(); var cacheKey = builder.BuildCacheKey(cacheContext); // TODO: Review this. Is there a way we can remove the use of the `ApplicationContext` singleton? // Get and cache the result return((TOuputType)ApplicationContext.Current.ApplicationCache.RuntimeCache.GetCacheItem( cacheKey, () => refresher(), priority: CacheItemPriority.NotRemovable, // Same as Umbraco macros timeout: new TimeSpan(0, 0, 0, this.CacheDuration))); }
/// <summary> /// Processes the value. /// </summary> /// <param name="value">The value.</param> /// <param name="context">The context.</param> /// <param name="chainContext">The chain context.</param> /// <returns> /// The <see cref="object" /> representing the processed value. /// </returns> internal virtual object ProcessValue( object value, DittoProcessorContext context, DittoChainContext chainContext) { if (value != null && this.ValueType.IsInstanceOfType(value) == false) { throw new ArgumentException($"Expected a value argument of type {this.ValueType} but got {value.GetType()}", "value"); } if (context == null) { throw new ArgumentNullException("context"); } if (this.ContextType.IsInstanceOfType(context) == false) { throw new ArgumentException($"Expected a context argument of type {this.ContextType} but got {context.GetType()}", "context"); } if (chainContext == null) { throw new ArgumentNullException("chainContext"); } this.Value = value; this.Context = context; this.ChainContext = chainContext; var ctx = new DittoCacheContext(this, context.Content, context.TargetType, context.PropertyInfo, context.Culture); return(this.GetCacheItem(ctx, this.ProcessValue)); }
/// <summary> /// Gets the cache item. /// </summary> /// <typeparam name="TOuputType">The type of the output type.</typeparam> /// <param name="cacheContext">The cache context.</param> /// <param name="refresher">The refresher.</param> /// <returns>Returns the output type.</returns> /// <exception cref="System.ApplicationException">Expected a cache key builder of type + typeof(DittoProcessorCacheKeyBuilder) + but got + CacheKeyBuilderType</exception> internal TOuputType GetCacheItem <TOuputType>(DittoCacheContext cacheContext, Func <TOuputType> refresher) { // If no cache duration set, just run the refresher if (this.CacheDuration == 0 || Ditto.IsDebuggingEnabled) { return(refresher()); } // Get the cache key builder type var cacheKeyBuilderType = this.CacheKeyBuilderType ?? typeof(DittoDefaultCacheKeyBuilder); // Check the cache key builder type if (!typeof(DittoCacheKeyBuilder).IsAssignableFrom(cacheKeyBuilderType)) { throw new ApplicationException($"Expected a cache key builder of type {typeof(DittoCacheKeyBuilder)} but got {this.CacheKeyBuilderType}"); } // Construct the cache key builder var builder = (DittoCacheKeyBuilder)cacheKeyBuilderType.GetInstance(); var cacheKey = builder.BuildCacheKey(cacheContext); // Get and cache the result return((TOuputType)ApplicationContext.Current.ApplicationCache.RuntimeCache.GetCacheItem( cacheKey, () => refresher(), priority: CacheItemPriority.NotRemovable, // Same as Umbraco macros timeout: new TimeSpan(0, 0, 0, this.CacheDuration))); }
/// <summary> /// Processes the value. /// </summary> /// <param name="value">The value.</param> /// <param name="context">The context.</param> /// <returns> /// The <see cref="object" /> representing the processed value. /// </returns> internal virtual object ProcessValue( object value, DittoProcessorContext context) { if (value != null && !ValueType.IsInstanceOfType(value)) { throw new ArgumentException("Expected a value argument of type " + ValueType + " but got " + value.GetType(), "value"); } if (context == null) { throw new ArgumentNullException("context"); } if (!ContextType.IsInstanceOfType(context)) { throw new ArgumentException("Expected a context argument of type " + ContextType + " but got " + context.GetType(), "context"); } Value = value; Context = context; var ctx = new DittoCacheContext(this, context.Content, context.TargetType, context.PropertyDescriptor, context.Culture); return(this.GetCacheItem(ctx, this.ProcessValue)); }
/// <summary> /// Returns the processed value for the given type and property. /// </summary> /// <param name="content">The <see cref="IPublishedContent" /> to convert.</param> /// <param name="culture">The <see cref="CultureInfo" /></param> /// <param name="targetType">The target type.</param> /// <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="processorContexts">A collection of <see cref="DittoProcessorContext" /> entities to use whilst processing values.</param> /// <returns> /// The <see cref="object" /> representing the Umbraco value. /// </returns> private static object GetProcessedValue( IPublishedContent content, CultureInfo culture, Type targetType, PropertyInfo propertyInfo, object instance, Type defaultProcessorType, IEnumerable <DittoProcessorContext> processorContexts = null) { // Time custom value-processor. using (DittoDisposableTimer.DebugDuration <object>(string.Format("Custom ValueProcessor ({0}, {1})", content.Id, propertyInfo.Name))) { // Get the target property description var propertyDescriptor = TypeDescriptor.GetProperties(instance)[propertyInfo.Name]; // Check for cache attribute var cacheAttr = propertyInfo.GetCustomAttribute <DittoCacheAttribute>(true); if (cacheAttr != null) { var ctx = new DittoCacheContext(cacheAttr, content, targetType, propertyDescriptor, culture); return(cacheAttr.GetCacheItem(ctx, () => DoGetProcessedValue(content, culture, targetType, propertyInfo, propertyDescriptor, defaultProcessorType, processorContexts))); } else { return(DoGetProcessedValue(content, culture, targetType, propertyInfo, propertyDescriptor, defaultProcessorType, processorContexts)); } } }
/// <summary> /// Returns an object representing the given <see cref="Type"/>. /// </summary> /// <param name="content"> /// The <see cref="IPublishedContent"/> to convert. /// </param> /// <param name="type"> /// The <see cref="Type"/> of items to return. /// </param> /// <param name="culture"> /// The <see cref="CultureInfo"/> /// </param> /// <param name="instance"> /// An existing instance of T to populate /// </param> /// <param name="processorContexts"> /// A collection of <see cref="DittoProcessorContext"/> entities to use whilst processing values. /// </param> /// <param name="onConverting"> /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converting. /// </param> /// <param name="onConverted"> /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converted. /// </param> /// <param name="chainContext"> /// The <see cref="DittoChainContext"/> for the current processor chain. /// </param> /// <returns> /// The converted <see cref="Object"/> as the given type. /// </returns> public static object As( this IPublishedContent content, Type type, CultureInfo culture = null, object instance = null, IEnumerable <DittoProcessorContext> processorContexts = null, Action <DittoConversionHandlerContext> onConverting = null, Action <DittoConversionHandlerContext> onConverted = null, DittoChainContext chainContext = null) { // Ensure content if (content == null) { return(null); } // Ensure instance is of target type if (instance != null && !type.IsInstanceOfType(instance)) { throw new ArgumentException($"The instance parameter does not implement Type '{type.Name}'", nameof(instance)); } // Get the context accessor (for access to ApplicationContext, UmbracoContext, et al) var contextAccessor = (IDittoContextAccessor)Ditto.GetContextAccessorType().GetInstance(); // Check if the culture has been set, otherwise use from Umbraco, or fallback to a default if (culture == null) { culture = contextAccessor.UmbracoContext?.PublishedContentRequest != null ? contextAccessor.UmbracoContext.PublishedContentRequest.Culture : CultureInfo.CurrentCulture; } // Ensure a chain context if (chainContext == null) { chainContext = new DittoChainContext(); } // Populate prcessor contexts collection with any passed in contexts chainContext.ProcessorContexts.AddRange(processorContexts); // Convert using (DittoDisposableTimer.DebugDuration <Ditto>($"As<{type.Name}>({content.DocumentTypeAlias} {content.Id})")) { var cacheAttr = type.GetCustomAttribute <DittoCacheAttribute>(true); if (cacheAttr != null) { var ctx = new DittoCacheContext(cacheAttr, content, type, culture); return(cacheAttr.GetCacheItem(ctx, () => ConvertContent(content, type, contextAccessor, culture, instance, onConverting, onConverted, chainContext))); } else { return(ConvertContent(content, type, contextAccessor, culture, instance, onConverting, onConverted, chainContext)); } } }
/// <summary>Returns an object representing the given <see cref="Type"/>.</summary> /// <param name="content">The <see cref="IPublishedContent"/> to convert.</param> /// <param name="type">The <see cref="Type"/> of items to return.</param> /// <param name="culture">The <see cref="CultureInfo"/></param> /// <param name="instance">An existing instance of T to populate</param> /// <param name="processorContexts">A collection of <see cref="DittoProcessorContext"/> entities to use whilst processing values.</param> /// <param name="onConverting">The <see cref="Action{ConversionHandlerContext}"/> to fire when converting.</param> /// <param name="onConverted">The <see cref="Action{ConversionHandlerContext}"/> to fire when converted.</param> /// <param name="chainContext">The <see cref="DittoChainContext"/> for the current processor chain.</param> /// <returns>The converted <see cref="Object"/> as the given type.</returns> public static object As( this IPublishedContent content, Type type, CultureInfo culture = null, object instance = null, IEnumerable <DittoProcessorContext> processorContexts = null, Action <DittoConversionHandlerContext> onConverting = null, Action <DittoConversionHandlerContext> onConverted = null, DittoChainContext chainContext = null) { // Ensure content if (content == null) { return(null); } // Ensure instance is of target type if (instance != null && type.IsInstanceOfType(instance) == false) { throw new ArgumentException($"The instance parameter does not implement Type '{type.Name}'", nameof(instance)); } // Check if the culture has been set, otherwise use from Umbraco, or fallback to a default if (culture == null) { culture = ContextAccessor?.UmbracoContext?.PublishedContentRequest?.Culture ?? CultureInfo.CurrentCulture; } // Ensure a chain context if (chainContext == null) { chainContext = new DittoChainContext(); } // Populate prcessor contexts collection with any passed in contexts chainContext.ProcessorContexts.AddRange(processorContexts); // Convert using (DittoDisposableTimer.DebugDuration(typeof(Ditto), $"As<{type.Name}>({content.DocumentTypeAlias} {content.Id})")) { var config = DittoTypeInfoCache.GetOrAdd(type); if (config.IsCacheable) { var ctx = new DittoCacheContext(config.CacheInfo, content, type, culture); return(config.CacheInfo.GetCacheItem(ctx, () => ConvertContent(content, config, culture, instance, onConverting, onConverted, chainContext))); } else { return(ConvertContent(content, config, culture, instance, onConverting, onConverted, chainContext)); } } }
/// <summary> /// Returns an object representing the given <see cref="Type"/>. /// </summary> /// <param name="content"> /// The <see cref="IPublishedContent"/> to convert. /// </param> /// <param name="type"> /// The <see cref="Type"/> of items to return. /// </param> /// <param name="culture"> /// The <see cref="CultureInfo"/> /// </param> /// <param name="instance"> /// An existing instance of T to populate /// </param> /// <param name="processorContexts"> /// A collection of <see cref="DittoProcessorContext"/> entities to use whilst processing values. /// </param> /// <param name="onConverting"> /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converting. /// </param> /// <param name="onConverted"> /// The <see cref="Action{ConversionHandlerContext}"/> to fire when converted. /// </param> /// <returns> /// The converted <see cref="Object"/> as the given type. /// </returns> public static object As( this IPublishedContent content, Type type, CultureInfo culture = null, object instance = null, IEnumerable <DittoProcessorContext> processorContexts = null, Action <DittoConversionHandlerContext> onConverting = null, Action <DittoConversionHandlerContext> onConverted = null) { // Ensure content if (content == null) { return(null); } // Ensure instance is of target type if (instance != null && !type.IsInstanceOfType(instance)) { throw new ArgumentException(string.Format("The instance parameter does not implement Type '{0}'", type.Name), "instance"); } // 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) { culture = UmbracoContext.Current.PublishedContentRequest.Culture; } else { // Fallback culture = CultureInfo.CurrentCulture; } } // Convert using (DittoDisposableTimer.DebugDuration <object>(string.Format("IPublishedContent As ({0})", content.DocumentTypeAlias))) { var cacheAttr = type.GetCustomAttribute <DittoCacheAttribute>(true); if (cacheAttr != null) { var ctx = new DittoCacheContext(cacheAttr, content, type, culture); return(cacheAttr.GetCacheItem(ctx, () => ConvertContent(content, type, culture, instance, processorContexts, onConverting, onConverted))); } else { return(ConvertContent(content, type, culture, instance, processorContexts, onConverting, onConverted)); } } }
/// <summary> /// Returns the processed value for the given type and property. /// </summary> /// <param name="content">The <see cref="IPublishedContent" /> to convert.</param> /// <param name="culture">The <see cref="CultureInfo" /></param> /// <param name="targetType">The target type.</param> /// <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="contextAccessor">The context accessor.</param> /// <param name="chainContext">The <see cref="DittoChainContext"/> for the current processor chain.</param> /// <returns> /// The <see cref="object" /> representing the Umbraco value. /// </returns> private static object GetProcessedValue( IPublishedContent content, CultureInfo culture, Type targetType, PropertyInfo propertyInfo, object instance, IDittoContextAccessor contextAccessor, DittoChainContext chainContext) { using (DittoDisposableTimer.DebugDuration <Ditto>($"Processing '{propertyInfo.Name}' ({content.Id})")) { // Get the target property description var propertyDescriptor = TypeDescriptor.GetProperties(instance)[propertyInfo.Name]; // Create a base processor context for this current chain level var baseProcessorContext = new DittoProcessorContext { Content = content, TargetType = targetType, PropertyDescriptor = propertyDescriptor, Culture = culture }; // Check for cache attribute var cacheAttr = propertyInfo.GetCustomAttribute <DittoCacheAttribute>(true); if (cacheAttr != null) { var ctx = new DittoCacheContext(cacheAttr, content, targetType, propertyDescriptor, culture); return(cacheAttr.GetCacheItem(ctx, () => DoGetProcessedValue(content, propertyInfo, contextAccessor, baseProcessorContext, chainContext))); } else { return(DoGetProcessedValue(content, propertyInfo, contextAccessor, baseProcessorContext, chainContext)); } } }