|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Composition.Diagnostics;
using System.Linq;
using System.Reflection;
namespace System.Composition.Convention
{
/// <summary>
/// Configures a type as a MEF part.
/// </summary>
public class PartConventionBuilder
{
private readonly Type[] _emptyTypeArray = Type.EmptyTypes;
private static List<Attribute> s_onImportsSatisfiedAttributeList;
private static readonly List<Attribute> s_importingConstructorList = new List<Attribute>() { new ImportingConstructorAttribute() };
private static readonly Type s_exportAttributeType = typeof(ExportAttribute);
private readonly List<ExportConventionBuilder> _typeExportBuilders;
private bool _isShared;
private string _sharingBoundary;
// Metadata selection
private List<Tuple<string, object>> _metadataItems;
private List<Tuple<string, Func<Type, object>>> _metadataItemFuncs;
// Constructor selector / configuration
private Func<IEnumerable<ConstructorInfo>, ConstructorInfo> _constructorFilter;
private Action<ParameterInfo, ImportConventionBuilder> _configureConstructorImports;
//Property Import/Export selection and configuration
private readonly List<Tuple<Predicate<PropertyInfo>, Action<PropertyInfo, ExportConventionBuilder>, Type>> _propertyExports;
private readonly List<Tuple<Predicate<PropertyInfo>, Action<PropertyInfo, ImportConventionBuilder>>> _propertyImports;
private readonly List<Tuple<Predicate<Type>, Action<Type, ExportConventionBuilder>>> _interfaceExports;
private readonly List<Predicate<MethodInfo>> _methodImportsSatisfiedNotifications;
internal Predicate<Type> SelectType { get; }
internal PartConventionBuilder(Predicate<Type> selectType)
{
SelectType = selectType;
_typeExportBuilders = new List<ExportConventionBuilder>();
_propertyExports = new List<Tuple<Predicate<PropertyInfo>, Action<PropertyInfo, ExportConventionBuilder>, Type>>();
_propertyImports = new List<Tuple<Predicate<PropertyInfo>, Action<PropertyInfo, ImportConventionBuilder>>>();
_interfaceExports = new List<Tuple<Predicate<Type>, Action<Type, ExportConventionBuilder>>>();
_methodImportsSatisfiedNotifications = new List<Predicate<MethodInfo>>();
}
/// <summary>
/// Export the part using its own concrete type as the contract.
/// </summary>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder Export()
{
var exportBuilder = new ExportConventionBuilder();
_typeExportBuilders.Add(exportBuilder);
return this;
}
/// <summary>
/// Export the part.
/// </summary>
/// <param name="exportConfiguration">Configuration action for the export.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder Export(Action<ExportConventionBuilder> exportConfiguration)
{
if (exportConfiguration is null)
{
throw new ArgumentNullException(nameof(exportConfiguration));
}
var exportBuilder = new ExportConventionBuilder();
exportConfiguration(exportBuilder);
_typeExportBuilders.Add(exportBuilder);
return this;
}
/// <summary>
/// Export the part using <typeparamref name="T"/> as the contract.
/// </summary>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder Export<T>()
{
ExportConventionBuilder exportBuilder = new ExportConventionBuilder().AsContractType<T>();
_typeExportBuilders.Add(exportBuilder);
return this;
}
/// <summary>
/// Export the class using <typeparamref name="T"/> as the contract.
/// </summary>
/// <param name="exportConfiguration">Configuration action for the export.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder Export<T>(Action<ExportConventionBuilder> exportConfiguration)
{
if (exportConfiguration is null)
{
throw new ArgumentNullException(nameof(exportConfiguration));
}
ExportConventionBuilder exportBuilder = new ExportConventionBuilder().AsContractType<T>();
exportConfiguration(exportBuilder);
_typeExportBuilders.Add(exportBuilder);
return this;
}
/// <summary>
/// Select which of the available constructors will be used to instantiate the part.
/// </summary>
/// <param name="constructorSelector">Filter that selects a single constructor.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder SelectConstructor(Func<IEnumerable<ConstructorInfo>, ConstructorInfo> constructorSelector)
{
if (constructorSelector is null)
{
throw new ArgumentNullException(nameof(constructorSelector));
}
_constructorFilter = constructorSelector;
return this;
}
/// <summary>
/// Select which of the available constructors will be used to instantiate the part.
/// </summary>
/// <param name="constructorSelector">Filter that selects a single constructor.</param>
/// <param name="importConfiguration">Action configuring the parameters of the selected constructor.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder SelectConstructor(Func<IEnumerable<ConstructorInfo>, ConstructorInfo> constructorSelector,
Action<ParameterInfo, ImportConventionBuilder> importConfiguration)
{
if (importConfiguration is null)
{
throw new ArgumentNullException(nameof(importConfiguration));
}
_configureConstructorImports = importConfiguration;
SelectConstructor(constructorSelector);
return this;
}
/// <summary>
/// Select the interfaces on the part type that will be exported.
/// </summary>
/// <param name="interfaceFilter">Filter for interfaces.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ExportInterfaces(Predicate<Type> interfaceFilter)
{
if (interfaceFilter is null)
{
throw new ArgumentNullException(nameof(interfaceFilter));
}
return ExportInterfacesImpl(interfaceFilter, null);
}
/// <summary>
/// Export all interfaces implemented by the part.
/// </summary>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ExportInterfaces()
{
return ExportInterfaces(t => true);
}
/// <summary>
/// Select the interfaces on the part type that will be exported.
/// </summary>
/// <param name="interfaceFilter">Filter for interfaces.</param>
/// <param name="exportConfiguration">Action to configure selected interfaces.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ExportInterfaces(Predicate<Type> interfaceFilter,
Action<Type, ExportConventionBuilder> exportConfiguration)
{
if (interfaceFilter is null)
{
throw new ArgumentNullException(nameof(interfaceFilter));
}
if (exportConfiguration is null)
{
throw new ArgumentNullException(nameof(exportConfiguration));
}
return ExportInterfacesImpl(interfaceFilter, exportConfiguration);
}
private PartConventionBuilder ExportInterfacesImpl(Predicate<Type> interfaceFilter,
Action<Type, ExportConventionBuilder> exportConfiguration)
{
_interfaceExports.Add(Tuple.Create(interfaceFilter, exportConfiguration));
return this;
}
/// <summary>
/// Select properties on the part to export.
/// </summary>
/// <param name="propertyFilter">Selector for exported properties.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ExportProperties(Predicate<PropertyInfo> propertyFilter)
{
if (propertyFilter is null)
{
throw new ArgumentNullException(nameof(propertyFilter));
}
return ExportPropertiesImpl(propertyFilter, null);
}
/// <summary>
/// Select properties on the part to export.
/// </summary>
/// <param name="propertyFilter">Selector for exported properties.</param>
/// <param name="exportConfiguration">Action to configure selected properties.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ExportProperties(Predicate<PropertyInfo> propertyFilter,
Action<PropertyInfo, ExportConventionBuilder> exportConfiguration)
{
if (propertyFilter is null)
{
throw new ArgumentNullException(nameof(propertyFilter));
}
if (exportConfiguration is null)
{
throw new ArgumentNullException(nameof(exportConfiguration));
}
return ExportPropertiesImpl(propertyFilter, exportConfiguration);
}
private PartConventionBuilder ExportPropertiesImpl(Predicate<PropertyInfo> propertyFilter,
Action<PropertyInfo, ExportConventionBuilder> exportConfiguration)
{
_propertyExports.Add(Tuple.Create(propertyFilter, exportConfiguration, default(Type)));
return this;
}
/// <summary>
/// Select properties to export from the part.
/// </summary>
/// <typeparam name="T">Contract type to export.</typeparam>
/// <param name="propertyFilter">Filter to select matching properties.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ExportProperties<T>(Predicate<PropertyInfo> propertyFilter)
{
if (propertyFilter is null)
{
throw new ArgumentNullException(nameof(propertyFilter));
}
return ExportPropertiesImpl<T>(propertyFilter, null);
}
/// <summary>
/// Select properties to export from the part.
/// </summary>
/// <typeparam name="T">Contract type to export.</typeparam>
/// <param name="propertyFilter">Filter to select matching properties.</param>
/// <param name="exportConfiguration">Action to configure selected properties.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ExportProperties<T>(Predicate<PropertyInfo> propertyFilter,
Action<PropertyInfo, ExportConventionBuilder> exportConfiguration)
{
if (propertyFilter is null)
{
throw new ArgumentNullException(nameof(propertyFilter));
}
if (exportConfiguration is null)
{
throw new ArgumentNullException(nameof(exportConfiguration));
}
return ExportPropertiesImpl<T>(propertyFilter, exportConfiguration);
}
private PartConventionBuilder ExportPropertiesImpl<T>(Predicate<PropertyInfo> propertyFilter,
Action<PropertyInfo, ExportConventionBuilder> exportConfiguration)
{
_propertyExports.Add(Tuple.Create(propertyFilter, exportConfiguration, typeof(T)));
return this;
}
/// <summary>
/// Select properties to import into the part.
/// </summary>
/// <param name="propertyFilter">Filter to select matching properties.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ImportProperties(Predicate<PropertyInfo> propertyFilter)
{
if (propertyFilter is null)
{
throw new ArgumentNullException(nameof(propertyFilter));
}
return ImportPropertiesImpl(propertyFilter, null);
}
/// <summary>
/// Select properties to import into the part.
/// </summary>
/// <param name="propertyFilter">Filter to select matching properties.</param>
/// <param name="importConfiguration">Action to configure selected properties.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ImportProperties(Predicate<PropertyInfo> propertyFilter,
Action<PropertyInfo, ImportConventionBuilder> importConfiguration)
{
if (propertyFilter is null)
{
throw new ArgumentNullException(nameof(propertyFilter));
}
if (importConfiguration is null)
{
throw new ArgumentNullException(nameof(importConfiguration));
}
return ImportPropertiesImpl(propertyFilter, importConfiguration);
}
private PartConventionBuilder ImportPropertiesImpl(Predicate<PropertyInfo> propertyFilter,
Action<PropertyInfo, ImportConventionBuilder> importConfiguration)
{
_propertyImports.Add(Tuple.Create(propertyFilter, importConfiguration));
return this;
}
/// <summary>
/// Select properties to import into the part.
/// </summary>
/// <typeparam name="T">Property type to import.</typeparam>
/// <param name="propertyFilter">Filter to select matching properties.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ImportProperties<T>(Predicate<PropertyInfo> propertyFilter)
{
if (propertyFilter is null)
{
throw new ArgumentNullException(nameof(propertyFilter));
}
return ImportPropertiesImpl<T>(propertyFilter, null);
}
/// <summary>
/// Select properties to import into the part.
/// </summary>
/// <typeparam name="T">Property type to import.</typeparam>
/// <param name="propertyFilter">Filter to select matching properties.</param>
/// <param name="importConfiguration">Action to configure selected properties.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder ImportProperties<T>(Predicate<PropertyInfo> propertyFilter,
Action<PropertyInfo, ImportConventionBuilder> importConfiguration)
{
if (propertyFilter is null)
{
throw new ArgumentNullException(nameof(propertyFilter));
}
if (importConfiguration is null)
{
throw new ArgumentNullException(nameof(importConfiguration));
}
return ImportPropertiesImpl<T>(propertyFilter, importConfiguration);
}
private PartConventionBuilder ImportPropertiesImpl<T>(Predicate<PropertyInfo> propertyFilter,
Action<PropertyInfo, ImportConventionBuilder> importConfiguration)
{
Predicate<PropertyInfo> typedFilter = pi => pi.PropertyType.Equals(typeof(T)) && (propertyFilter == null || propertyFilter(pi));
_propertyImports.Add(Tuple.Create(typedFilter, importConfiguration));
return this;
}
/// <summary>
/// Mark the part as being shared within the entire composition.
/// </summary>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder NotifyImportsSatisfied(Predicate<MethodInfo> methodFilter)
{
_methodImportsSatisfiedNotifications.Add(methodFilter);
return this;
}
/// <summary>
/// Mark the part as being shared within the entire composition.
/// </summary>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder Shared()
{
return SharedImpl(null);
}
/// <summary>
/// Mark the part as being shared within the specified boundary.
/// </summary>
/// <param name="sharingBoundary">Name of the sharing boundary.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder Shared(string sharingBoundary)
{
if (sharingBoundary is null)
{
throw new ArgumentNullException(nameof(sharingBoundary));
}
if (sharingBoundary.Length == 0)
{
throw new ArgumentException(SR.ArgumentException_EmptyString);
}
return SharedImpl(sharingBoundary);
}
private PartConventionBuilder SharedImpl(string sharingBoundary)
{
_isShared = true;
_sharingBoundary = sharingBoundary;
return this;
}
/// <summary>
/// Add the specified metadata to the part.
/// </summary>
/// <param name="name">The metadata name.</param>
/// <param name="value">The metadata value.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder AddPartMetadata(string name, object value)
{
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
if (name.Length == 0)
{
throw new ArgumentException(SR.Format(SR.ArgumentException_EmptyString, nameof(name)), nameof(name));
}
_metadataItems ??= new List<Tuple<string, object>>();
_metadataItems.Add(Tuple.Create(name, value));
return this;
}
/// <summary>
/// Add the specified metadata to the part.
/// </summary>
/// <param name="name">The metadata name.</param>
/// <param name="getValueFromPartType">A function mapping the part type to the metadata value.</param>
/// <returns>A part builder allowing further configuration of the part.</returns>
public PartConventionBuilder AddPartMetadata(string name, Func<Type, object> getValueFromPartType)
{
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
if (getValueFromPartType is null)
{
throw new ArgumentNullException(nameof(getValueFromPartType));
}
if (name.Length == 0)
{
throw new ArgumentException(SR.Format(SR.ArgumentException_EmptyString, nameof(name)), nameof(name));
}
_metadataItemFuncs ??= new List<Tuple<string, Func<Type, object>>>();
_metadataItemFuncs.Add(Tuple.Create(name, getValueFromPartType));
return this;
}
private static bool MemberHasExportMetadata(MemberInfo member)
{
foreach (Attribute attr in member.GetCustomAttributes<Attribute>(false))
{
if (attr is ExportMetadataAttribute)
{
return true;
}
else
{
Type attrType = attr.GetType();
// Perf optimization, relies on short circuit evaluation, often a property attribute is an ExportAttribute
if (attrType != s_exportAttributeType && attrType.GetTypeInfo().IsDefined(typeof(MetadataAttributeAttribute), true))
{
return true;
}
}
}
return false;
}
internal IEnumerable<Attribute> BuildTypeAttributes(Type type)
{
var attributes = new List<Attribute>();
if (_typeExportBuilders != null)
{
bool isConfigured = type.GetTypeInfo().GetCustomAttributes<ExportAttribute>(false).FirstOrDefault() != null || MemberHasExportMetadata(type.GetTypeInfo());
if (isConfigured)
{
CompositionTrace.Registration_TypeExportConventionOverridden(type);
}
else
{
foreach (ExportConventionBuilder export in _typeExportBuilders)
{
export.BuildAttributes(type, ref attributes);
}
}
}
if (_isShared)
{
// Check if there is already a SharedAttribute. If found Trace a warning and do not add this Shared
// otherwise add new one
bool isConfigured = type.GetTypeInfo().GetCustomAttributes<SharedAttribute>(false).FirstOrDefault() != null;
if (isConfigured)
{
CompositionTrace.Registration_PartCreationConventionOverridden(type);
}
else
{
attributes.Add(_sharingBoundary == null ?
new SharedAttribute() :
new SharedAttribute(_sharingBoundary));
}
}
//Add metadata attributes from direct specification
if (_metadataItems != null)
{
bool isConfigured = type.GetTypeInfo().GetCustomAttributes<PartMetadataAttribute>(false).FirstOrDefault() != null;
if (isConfigured)
{
CompositionTrace.Registration_PartMetadataConventionOverridden(type);
}
else
{
foreach (Tuple<string, object> item in _metadataItems)
{
attributes.Add(new PartMetadataAttribute(item.Item1, item.Item2));
}
}
}
//Add metadata attributes from func specification
if (_metadataItemFuncs != null)
{
bool isConfigured = type.GetTypeInfo().GetCustomAttributes<PartMetadataAttribute>(false).FirstOrDefault() != null;
if (isConfigured)
{
CompositionTrace.Registration_PartMetadataConventionOverridden(type);
}
else
{
foreach (Tuple<string, Func<Type, object>> item in _metadataItemFuncs)
{
var name = item.Item1;
var value = (item.Item2 != null) ? item.Item2(type) : null;
attributes.Add(new PartMetadataAttribute(name, value));
}
}
}
if (_interfaceExports.Count != 0)
{
if (_typeExportBuilders != null)
{
bool isConfigured = type.GetTypeInfo().GetCustomAttributes<ExportAttribute>(false).FirstOrDefault() != null || MemberHasExportMetadata(type.GetTypeInfo());
if (isConfigured)
{
CompositionTrace.Registration_TypeExportConventionOverridden(type);
}
else
{
foreach (Type iface in type.GetTypeInfo().ImplementedInterfaces)
{
if (iface == typeof(IDisposable))
{
continue;
}
// Run through the export specifications see if any match
foreach (Tuple<Predicate<Type>, Action<Type, ExportConventionBuilder>> exportSpecification in _interfaceExports)
{
if (exportSpecification.Item1 != null && exportSpecification.Item1(iface))
{
ExportConventionBuilder exportBuilder = new ExportConventionBuilder();
exportBuilder.AsContractType(iface);
exportSpecification.Item2?.Invoke(iface, exportBuilder);
exportBuilder.BuildAttributes(iface, ref attributes);
}
}
}
}
}
}
return attributes;
}
internal bool BuildConstructorAttributes(Type type, ref List<Tuple<object, List<Attribute>>> configuredMembers)
{
ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
// First see if any of these constructors have the ImportingConstructorAttribute if so then we are already done
foreach (ConstructorInfo ci in constructors)
{
// We have a constructor configuration we must log a warning then not bother with ConstructorAttributes
Attribute[] attributes = Attribute.GetCustomAttributes(ci, typeof(ImportingConstructorAttribute), false);
if (attributes.Length != 0)
{
CompositionTrace.Registration_ConstructorConventionOverridden(type);
return true;
}
}
if (_constructorFilter != null)
{
ConstructorInfo constructorInfo = _constructorFilter(constructors);
if (constructorInfo != null)
{
ConfigureConstructorAttributes(constructorInfo, ref configuredMembers, _configureConstructorImports);
}
return true;
}
else if (_configureConstructorImports != null)
{
bool configured = false;
foreach (ConstructorInfo constructorInfo in FindLongestConstructors(constructors))
{
ConfigureConstructorAttributes(constructorInfo, ref configuredMembers, _configureConstructorImports);
configured = true;
}
return configured;
}
return false;
}
internal static void BuildDefaultConstructorAttributes(Type type, ref List<Tuple<object, List<Attribute>>> configuredMembers)
{
ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (ConstructorInfo constructorInfo in FindLongestConstructors(constructors))
{
ConfigureConstructorAttributes(constructorInfo, ref configuredMembers, null);
}
}
private static void ConfigureConstructorAttributes(ConstructorInfo constructorInfo, ref List<Tuple<object, List<Attribute>>> configuredMembers, Action<ParameterInfo, ImportConventionBuilder> configureConstructorImports)
{
configuredMembers ??= new List<Tuple<object, List<Attribute>>>();
// Make its attribute
configuredMembers.Add(Tuple.Create((object)constructorInfo, s_importingConstructorList));
//Okay we have the constructor now we can configure the ImportBuilders
ParameterInfo[] parameterInfos = constructorInfo.GetParameters();
foreach (ParameterInfo pi in parameterInfos)
{
bool isConfigured = pi.GetCustomAttributes<ImportAttribute>(false).FirstOrDefault() != null || pi.GetCustomAttributes<ImportManyAttribute>(false).FirstOrDefault() != null;
if (isConfigured)
{
CompositionTrace.Registration_ParameterImportConventionOverridden(pi, constructorInfo);
}
else
{
var importBuilder = new ImportConventionBuilder();
// Let the developer alter them if they specified to do so
configureConstructorImports?.Invoke(pi, importBuilder);
// Generate the attributes
List<Attribute> attributes = null;
importBuilder.BuildAttributes(pi.ParameterType, ref attributes);
configuredMembers.Add(Tuple.Create((object)pi, attributes));
}
}
}
internal void BuildOnImportsSatisfiedNotification(Type type, ref List<Tuple<object, List<Attribute>>> configuredMembers)
{
//Add OnImportsSatisfiedAttribute where specified
if (_methodImportsSatisfiedNotifications != null)
{
foreach (MethodInfo mi in type.GetRuntimeMethods())
{
//We are only interested in void methods with no arguments
if (mi.ReturnParameter.ParameterType == typeof(void)
&& mi.GetParameters().Length == 0)
{
MethodInfo underlyingMi = mi.DeclaringType.GetRuntimeMethod(mi.Name, _emptyTypeArray);
if (underlyingMi != null)
{
bool checkedIfConfigured = false;
bool isConfigured = false;
foreach (Predicate<MethodInfo> notification in _methodImportsSatisfiedNotifications)
{
if (notification(underlyingMi))
{
if (!checkedIfConfigured)
{
isConfigured = mi.GetCustomAttributes<OnImportsSatisfiedAttribute>(false).FirstOrDefault() != null;
checkedIfConfigured = true;
}
if (isConfigured)
{
CompositionTrace.Registration_OnSatisfiedImportNotificationOverridden(type, mi);
break;
}
else
{
// We really only need to create this list once and then cache it, it never goes back to null
// Its perfectly okay if we make a list a few times on different threads, effectively though once we have
// cached one we will never make another.
if (s_onImportsSatisfiedAttributeList == null)
{
var onImportsSatisfiedAttributeList = new List<Attribute>
{
new OnImportsSatisfiedAttribute()
};
s_onImportsSatisfiedAttributeList = onImportsSatisfiedAttributeList;
}
configuredMembers.Add(new Tuple<object, List<Attribute>>(mi, s_onImportsSatisfiedAttributeList));
}
}
}
}
}
}
}
}
internal void BuildPropertyAttributes(Type type, ref List<Tuple<object, List<Attribute>>> configuredMembers)
{
if (_propertyImports.Count != 0 || _propertyExports.Count != 0)
{
foreach (PropertyInfo pi in type.GetRuntimeProperties())
{
List<Attribute> attributes = null;
int importsBuilt = 0;
bool checkedIfConfigured = false;
bool isConfigured = false;
PropertyInfo underlyingPi = null;
// Run through the import specifications see if any match
foreach (Tuple<Predicate<PropertyInfo>, Action<PropertyInfo, ImportConventionBuilder>> importSpecification in _propertyImports)
{
underlyingPi ??= pi.DeclaringType.GetRuntimeProperty(pi.Name);
if (importSpecification.Item1 != null && importSpecification.Item1(underlyingPi))
{
var importBuilder = new ImportConventionBuilder();
importSpecification.Item2?.Invoke(pi, importBuilder);
if (!checkedIfConfigured)
{
isConfigured = pi.GetCustomAttributes<ImportAttribute>(false).FirstOrDefault() != null || pi.GetCustomAttributes<ImportManyAttribute>(false).FirstOrDefault() != null;
checkedIfConfigured = true;
}
if (isConfigured)
{
CompositionTrace.Registration_MemberImportConventionOverridden(type, pi);
break;
}
else
{
importBuilder.BuildAttributes(pi.PropertyType, ref attributes);
++importsBuilt;
}
}
if (importsBuilt > 1)
{
CompositionTrace.Registration_MemberImportConventionMatchedTwice(type, pi);
}
}
checkedIfConfigured = false;
isConfigured = false;
// Run through the export specifications see if any match
foreach (Tuple<Predicate<PropertyInfo>, Action<PropertyInfo, ExportConventionBuilder>, Type> exportSpecification in _propertyExports)
{
underlyingPi ??= pi.DeclaringType.GetRuntimeProperty(pi.Name);
if (exportSpecification.Item1 != null && exportSpecification.Item1(underlyingPi))
{
var exportBuilder = new ExportConventionBuilder();
if (exportSpecification.Item3 != null)
{
exportBuilder.AsContractType(exportSpecification.Item3);
}
exportSpecification.Item2?.Invoke(pi, exportBuilder);
if (!checkedIfConfigured)
{
isConfigured = pi.GetCustomAttributes<ExportAttribute>(false).FirstOrDefault() != null || MemberHasExportMetadata(pi);
checkedIfConfigured = true;
}
if (isConfigured)
{
CompositionTrace.Registration_MemberExportConventionOverridden(type, pi);
break;
}
else
{
exportBuilder.BuildAttributes(pi.PropertyType, ref attributes);
}
}
}
if (attributes != null)
{
configuredMembers ??= new List<Tuple<object, List<Attribute>>>();
configuredMembers.Add(Tuple.Create((object)pi, attributes));
}
}
}
return;
}
private static IEnumerable<ConstructorInfo> FindLongestConstructors(ConstructorInfo[] constructors)
{
ConstructorInfo longestConstructor = null;
int argumentsCount = 0;
int constructorsFound = 0;
foreach (ConstructorInfo candidateConstructor in constructors)
{
int length = candidateConstructor.GetParameters().Length;
if (length != 0)
{
if (length > argumentsCount)
{
longestConstructor = candidateConstructor;
argumentsCount = length;
constructorsFound = 1;
}
else if (length == argumentsCount)
{
++constructorsFound;
}
}
}
if (constructorsFound > 1)
{
foreach (ConstructorInfo candidateConstructor in constructors)
{
int length = candidateConstructor.GetParameters().Length;
if (length == argumentsCount)
{
yield return candidateConstructor;
}
}
}
else if (constructorsFound == 1)
{
yield return longestConstructor;
}
yield break;
}
}
}
|