using System; using System.Collections.Generic; using System.Linq.Expressions; namespace Umbraco.Core.Models.PublishedContent { /// /// Implements a strongly typed content model factory /// public class PublishedContentModelFactory : IPublishedContentModelFactory { //private readonly Dictionary _constructors // = new Dictionary(); private readonly Dictionary> _constructors; /// /// Initializes a new instance of the class with types. /// /// The model types. /// /// Types must implement IPublishedContent and have a unique constructor that /// accepts one IPublishedContent as a parameter. /// To activate, /// /// var types = PluginManager.Current.ResolveTypes{PublishedContentModel}(); /// var factory = new PublishedContentModelFactoryImpl(types); /// PublishedContentModelFactoryResolver.Current.SetFactory(factory); /// /// public PublishedContentModelFactory(IEnumerable types) { var ctorArgTypes = new[] { typeof(IPublishedContent) }; var constructors = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); foreach (var type in types) { var constructor = type.GetConstructor(ctorArgTypes); if (constructor == null) throw new InvalidOperationException($"Type {type.FullName} is missing a public constructor with one argument of type IPublishedContent."); var attribute = type.GetCustomAttribute(false); var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias; if (constructors.ContainsKey(typeName)) throw new InvalidOperationException($"More that one type want to be a model for content type {typeName}."); // should work everywhere, but slow //_constructors[typeName] = constructor; // much faster with a dynamic method but potential MediumTrust issues // here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object // fast enough and works in MediumTrust // read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/ var exprArg = Expression.Parameter(typeof(IPublishedContent), "content"); var exprNew = Expression.New(constructor, exprArg); var expr = Expression.Lambda>(exprNew, exprArg); var func = expr.Compile(); constructors[typeName] = func; } _constructors = constructors.Count > 0 ? constructors : null; } public IPublishedContent CreateModel(IPublishedContent content) { // fail fast if (_constructors == null) return content; // be case-insensitive var contentTypeAlias = content.DocumentTypeAlias; //ConstructorInfo constructor; //return _constructors.TryGetValue(contentTypeAlias, out constructor) // ? (IPublishedContent) constructor.Invoke(new object[] { content }) // : content; Func constructor; return _constructors.TryGetValue(contentTypeAlias, out constructor) ? constructor(content) : content; } public T CreateModel(IPublishedFragment content) { throw new NotImplementedException(); } } }