Skip to content

Instantly share code, notes, and snippets.

@Hendy
Last active January 19, 2020 21:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Hendy/da5d8a67e1711d195c9a to your computer and use it in GitHub Desktop.
Save Hendy/da5d8a67e1711d195c9a to your computer and use it in GitHub Desktop.
Ditto That Ditto
using Our.Umbraco.Ditto;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using Umbraco.Core.Models;
namespace Example.TypeConverters
{
/* Ditto that Ditto
* If the value supplied to Ditto is IPublishedContent, or a collection of IPublishedContent
* (eg as returned by Nested Content) and the return type on the model property is a different
* object (or collection of), then attempt to re-Ditto it. Eg:
*
* [TypeConverter(typeof(PublishedContentTypeConverter))]
* public IEnumerable<MyPoco> MyProperty { get; set; }
*
* so if the value supplied to Ditto for MyProperty is a collection of IPublishedContent,
* then this type converter will attempt to convert IEnumerable<IPublishedContent>
* into IEnumerable<MyPoco>
*/
public class PublishedContentTypeConverter : TypeConverter
{
/// <summary>
/// check to see if this can perform the conversion
/// </summary>
/// <param name="context">contains the specification of the output type</param>
/// <param name="sourceType">the input type</param>
/// <returns>true if this type converter can do the conversion</returns>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return
(
typeof(IEnumerable<IPublishedContent>).IsAssignableFrom(sourceType)
&& context.PropertyDescriptor.PropertyType.IsGenericType
&& context.PropertyDescriptor.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
&& context.PropertyDescriptor.PropertyType.GenericTypeArguments[0] != sourceType.GenericTypeArguments[0]
)
||
(
typeof(IPublishedContent).IsAssignableFrom(sourceType)
&& !context.PropertyDescriptor.PropertyType.IsGenericType
&& context.PropertyDescriptor.PropertyType != sourceType
);
}
/// <summary>
/// attempt to perform the conversion
/// </summary>
/// <param name="context">contains the specification of the output type</param>
/// <param name="culture"></param>
/// <param name="value">expected to an IEnumerable of IPublishedContent or a single IPublishedContent</param>
/// <returns>null, or a single item, or a list of items as specified by the model property return type</returns>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
MethodInfo dittoAs = typeof(PublishedContentExtensions)
.GetMethod(
"As",
BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly,
null,
new Type[] {
typeof(IPublishedContent),
typeof(Action<ConvertingTypeEventArgs>),
typeof(Action<ConvertedTypeEventArgs>),
typeof(CultureInfo) },
null);
if (typeof(IEnumerable<IPublishedContent>).IsAssignableFrom(value.GetType()))
{
// get type of objects to be returned in the collection
Type outputItemType = context.PropertyDescriptor.PropertyType.GenericTypeArguments[0];
// create a list for the output collection
object outputList = Activator.CreateInstance(typeof(List<>).MakeGenericType(outputItemType));
foreach (IPublishedContent inputItem in (IEnumerable<IPublishedContent>)value)
{
((IList)outputList).Add(
dittoAs
.MakeGenericMethod(new[] { outputItemType })
.Invoke(null, new[] { inputItem, null, null, null })
);
}
return outputList;
}
else if(typeof(IPublishedContent).IsAssignableFrom(value.GetType()))
{
return dittoAs
.MakeGenericMethod(new[] { context.PropertyDescriptor.PropertyType })
.Invoke(null, new[] { value, null, null, null });
}
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment