/// <summary> /// Initializes a new instance of the <see cref="PublishedModelFactory" /> class with types. /// </summary> /// <param name="types">The model types.</param> /// <param name="publishedValueFallback"></param> /// <remarks> /// <para> /// Types must implement <c>IPublishedContent</c> and have a unique constructor that /// accepts one IPublishedContent as a parameter. /// </para> /// <para>To activate,</para> /// <code> /// var types = TypeLoader.Current.GetTypes{PublishedContentModel}(); /// var factory = new PublishedContentModelFactoryImpl(types); /// PublishedContentModelFactoryResolver.Current.SetFactory(factory); /// </code> /// </remarks> public PublishedModelFactory(IEnumerable <Type> types, IPublishedValueFallback publishedValueFallback) { var modelInfos = new Dictionary <string, ModelInfo>(StringComparer.InvariantCultureIgnoreCase); var modelTypeMap = new Dictionary <string, Type>(StringComparer.InvariantCultureIgnoreCase); foreach (Type type in types) { // so... the model type has to implement a ctor with one parameter being, or inheriting from, // IPublishedElement - but it can be IPublishedContent - so we cannot get one precise ctor, // we have to iterate over all ctors and try to find the right one ConstructorInfo?constructor = null; Type? parameterType = null; foreach (ConstructorInfo ctor in type.GetConstructors()) { ParameterInfo[] parms = ctor.GetParameters(); if (parms.Length == 2 && typeof(IPublishedElement).IsAssignableFrom(parms[0].ParameterType) && typeof(IPublishedValueFallback).IsAssignableFrom(parms[1].ParameterType)) { if (constructor != null) { throw new InvalidOperationException( $"Type {type.FullName} has more than one public constructor with one argument of type, or implementing, IPublishedElement."); } constructor = ctor; parameterType = parms[0].ParameterType; } } if (constructor == null) { throw new InvalidOperationException( $"Type {type.FullName} is missing a public constructor with one argument of type, or implementing, IPublishedElement."); } PublishedModelAttribute?attribute = type.GetCustomAttribute <PublishedModelAttribute>(false); var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias; if (modelInfos.TryGetValue(typeName, out ModelInfo? modelInfo)) { throw new InvalidOperationException( $"Both types '{type.AssemblyQualifiedName}' and '{modelInfo.ModelType?.AssemblyQualifiedName}' want to be a model type for content type with alias \"{typeName}\"."); } // have to use an unsafe ctor because we don't know the types, really Func <object, IPublishedValueFallback, object> modelCtor = ReflectionUtilities.EmitConstructorUnsafe <Func <object, IPublishedValueFallback, object> >(constructor); modelInfos[typeName] = new ModelInfo { ParameterType = parameterType, ModelType = type, Ctor = modelCtor }; modelTypeMap[typeName] = type; } _modelInfos = modelInfos.Count > 0 ? modelInfos : null; _modelTypeMap = modelTypeMap; _publishedValueFallback = publishedValueFallback; }