in src/Microsoft.VisualStudio.Composition/Configuration/AttributedPartDiscovery.cs [49:246]
protected override ComposablePartDefinition? CreatePart(Type partType, bool typeExplicitlyRequested)
{
Requires.NotNull(partType, nameof(partType));
var partTypeInfo = partType.GetTypeInfo();
if (!typeExplicitlyRequested)
{
bool isPublic = partType.IsNested ? partTypeInfo.IsNestedPublic : partTypeInfo.IsPublic;
if (!this.IsNonPublicSupported && !isPublic)
{
// Skip non-public types.
return null;
}
}
BindingFlags instanceLocal = BindingFlags.DeclaredOnly | BindingFlags.Instance | this.PublicVsNonPublicFlags;
var declaredProperties = partTypeInfo.GetProperties(instanceLocal);
var exportingProperties = from member in declaredProperties
from export in member.GetAttributes<ExportAttribute>()
where member.GetMethod != null // MEFv2 quietly omits exporting properties with no getter
select new KeyValuePair<MemberInfo, ExportAttribute>(member, export);
var exportedTypes = from export in partTypeInfo.GetAttributes<ExportAttribute>()
select new KeyValuePair<MemberInfo, ExportAttribute>(partTypeInfo, export);
var exportsByMember = (from export in exportingProperties.Concat(exportedTypes)
group export.Value by export.Key into exportsByType
select exportsByType).Select(g => new KeyValuePair<MemberInfo, ExportAttribute[]>(g.Key, g.ToArray())).ToArray();
if (exportsByMember.Length == 0)
{
return null;
}
// Check for PartNotDiscoverable only after we've established it's an interesting part.
// This optimizes for the fact that most types have no exports, in which case it's not a discoverable
// part anyway. Checking for the PartNotDiscoverableAttribute first, which is rarely defined,
// doesn't usually pay for itself in terms of short-circuiting. But it does add an extra
// attribute to look for that we don't need to find for all the types that have no export attributes either.
if (!typeExplicitlyRequested && partTypeInfo.IsAttributeDefined<PartNotDiscoverableAttribute>())
{
return null;
}
foreach (var exportingMember in exportsByMember)
{
this.ThrowOnInvalidExportingMember(exportingMember.Key);
}
TypeRef partTypeRef = TypeRef.Get(partType, this.Resolver);
Type? partTypeAsGenericTypeDefinition = partTypeInfo.IsGenericType ? partType.GetGenericTypeDefinition() : null;
string? sharingBoundary = null;
var sharedAttribute = partTypeInfo.GetFirstAttribute<SharedAttribute>();
if (sharedAttribute != null)
{
sharingBoundary = sharedAttribute.SharingBoundary ?? string.Empty;
}
CreationPolicy partCreationPolicy = sharingBoundary != null ? CreationPolicy.Shared : CreationPolicy.NonShared;
var allExportsMetadata = ImmutableDictionary.CreateRange(PartCreationPolicyConstraint.GetExportMetadata(partCreationPolicy));
var exportsOnType = ImmutableList.CreateBuilder<ExportDefinition>();
var exportsOnMembers = ImmutableDictionary.CreateBuilder<MemberRef, IReadOnlyCollection<ExportDefinition>>();
foreach (var export in exportsByMember)
{
var member = export.Key;
var memberExportMetadata = allExportsMetadata.AddRange(this.GetExportMetadata(member));
if (member is TypeInfo)
{
foreach (var exportAttribute in export.Value)
{
Type exportedType = exportAttribute.ContractType ?? partTypeAsGenericTypeDefinition ?? partType;
ExportDefinition exportDefinition = CreateExportDefinition(memberExportMetadata, exportAttribute, exportedType);
exportsOnType.Add(exportDefinition);
}
}
else // property
{
var property = (PropertyInfo)member;
Verify.Operation(!partTypeInfo.IsGenericTypeDefinition, Strings.ExportsOnMembersNotAllowedWhenDeclaringTypeGeneric);
var exportDefinitions = ImmutableList.CreateBuilder<ExportDefinition>();
foreach (var exportAttribute in export.Value)
{
Type exportedType = exportAttribute.ContractType ?? property.PropertyType;
ExportDefinition exportDefinition = CreateExportDefinition(memberExportMetadata, exportAttribute, exportedType);
exportDefinitions.Add(exportDefinition);
}
exportsOnMembers.Add(MemberRef.Get(member, this.Resolver), exportDefinitions.ToImmutable());
}
}
var imports = ImmutableList.CreateBuilder<ImportDefinitionBinding>();
AddImportsFromMembers(declaredProperties, partTypeRef, imports);
Type? baseType = partTypeInfo.BaseType;
while (baseType != null && baseType != typeof(object))
{
AddImportsFromMembers(baseType.GetProperties(instanceLocal), partTypeRef, imports);
baseType = baseType.GetTypeInfo().BaseType;
}
void AddImportsFromMembers(PropertyInfo[] declaredProperties, TypeRef partTypeRef, IList<ImportDefinitionBinding> imports)
{
foreach (var member in declaredProperties)
{
try
{
var importAttribute = member.GetFirstAttribute<ImportAttribute>();
var importManyAttribute = member.GetFirstAttribute<ImportManyAttribute>();
Requires.Argument(!(importAttribute != null && importManyAttribute != null), nameof(partType), Strings.MemberContainsBothImportAndImportMany, member.Name);
var importConstraints = GetImportConstraints(member);
ImportDefinition? importDefinition;
if (this.TryCreateImportDefinition(ReflectionHelpers.GetMemberType(member), member, importConstraints, out importDefinition))
{
var importDefinitionBinding = new ImportDefinitionBinding(
importDefinition,
partTypeRef,
MemberRef.Get(member, this.Resolver),
TypeRef.Get(member.PropertyType, this.Resolver),
TypeRef.Get(GetImportingSiteTypeWithoutCollection(importDefinition, member.PropertyType), this.Resolver));
imports.Add(importDefinitionBinding);
}
}
catch (Exception ex)
{
throw ThrowErrorScanningMember(member, ex);
}
}
}
// MEFv2 is willing to find `internal` OnImportsSatisfied methods, so we should too regardless of our NonPublic flag.
var onImportsSatisfied = ImmutableList.CreateBuilder<MethodRef>();
Type? currentType = partTypeInfo;
while (currentType is object && currentType != typeof(object))
{
foreach (var method in currentType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
try
{
if (method.IsAttributeDefined<OnImportsSatisfiedAttribute>())
{
Verify.Operation(method.GetParameters().Length == 0, Strings.OnImportsSatisfiedTakeNoParameters);
onImportsSatisfied.Add(MethodRef.Get(method, this.Resolver));
}
}
catch (Exception ex)
{
throw ThrowErrorScanningMember(method, ex);
}
}
currentType = currentType.GetTypeInfo().BaseType;
}
var importingConstructorParameters = ImmutableList.CreateBuilder<ImportDefinitionBinding>();
var importingCtor = GetImportingConstructor<ImportingConstructorAttribute>(partType, publicOnly: !this.IsNonPublicSupported);
Verify.Operation(importingCtor != null, Strings.NoImportingConstructorFound);
foreach (var parameter in importingCtor.GetParameters())
{
var import = this.CreateImport(parameter, GetImportConstraints(parameter));
if (import.ImportDefinition.Cardinality == ImportCardinality.ZeroOrMore)
{
Verify.Operation(PartDiscovery.IsImportManyCollectionTypeCreateable(import), Strings.CollectionMustBePublicAndPublicCtorWhenUsingImportingCtor);
}
importingConstructorParameters.Add(import);
}
var partMetadata = ImmutableDictionary.CreateBuilder<string, object?>();
foreach (var partMetadataAttribute in partTypeInfo.GetAttributes<PartMetadataAttribute>())
{
partMetadata[partMetadataAttribute.Name] = partMetadataAttribute.Value;
}
var assemblyNamesForMetadataAttributes = ImmutableHashSet.CreateBuilder<AssemblyName>(ByValueEquality.AssemblyName);
foreach (var export in exportsByMember)
{
GetAssemblyNamesFromMetadataAttributes<MetadataAttributeAttribute>(export.Key, assemblyNamesForMetadataAttributes);
}
return new ComposablePartDefinition(
TypeRef.Get(partType, this.Resolver),
partMetadata.ToImmutable(),
exportsOnType.ToImmutable(),
exportsOnMembers.ToImmutable(),
imports.ToImmutable(),
sharingBoundary,
onImportsSatisfied.ToImmutable(),
MethodRef.Get(importingCtor, this.Resolver),
importingConstructorParameters.ToImmutable(),
partCreationPolicy,
isSharingBoundaryInferred: false,
extraInputAssemblies: assemblyNamesForMetadataAttributes);
static Exception ThrowErrorScanningMember(MemberInfo member, Exception ex) => throw new PartDiscoveryException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorWhileScanningMember, member.Name), ex);
}