diff --git a/src/Shared/RoslynUtils/WellKnownTypes.cs b/src/Shared/RoslynUtils/WellKnownTypes.cs
index a3a5cd3061b7..05925ddf68bc 100644
--- a/src/Shared/RoslynUtils/WellKnownTypes.cs
+++ b/src/Shared/RoslynUtils/WellKnownTypes.cs
@@ -58,7 +58,28 @@ public INamedTypeSymbol Get(SpecialType type)
return _compilation.GetSpecialType(type);
}
+ ///
+ /// Returns the type symbol for the specified well-known type, or throws if the type cannot be found.
+ ///
public INamedTypeSymbol Get(WellKnownTypeData.WellKnownType type)
+ {
+ return Get(type, throwOnNotFound: true);
+ }
+
+ ///
+ /// Returns the type symbol for the specified well-known type, or a special marker type symbol if the type cannot be found.
+ ///
+ ///
+ /// We use a special marker type for cases where some types can be legitimately missing.
+ /// E.g. The Microsoft.Extensions.Validation source generator checks against some types
+ /// from the shared framework which are missing in Blazor WebAssembly SDK projects.
+ ///
+ public INamedTypeSymbol GetOptional(WellKnownTypeData.WellKnownType type)
+ {
+ return Get(type, throwOnNotFound: false);
+ }
+
+ private INamedTypeSymbol Get(WellKnownTypeData.WellKnownType type, bool throwOnNotFound)
{
var index = (int)type;
var symbol = _lazyWellKnownTypes[index];
@@ -69,16 +90,22 @@ public INamedTypeSymbol Get(WellKnownTypeData.WellKnownType type)
// Symbol hasn't been added to the cache yet.
// Resolve symbol from name, cache, and return.
- return GetAndCache(index);
+ return GetAndCache(index, throwOnNotFound);
}
- private INamedTypeSymbol GetAndCache(int index)
+ private INamedTypeSymbol GetAndCache(int index, bool throwOnNotFound)
{
var result = GetTypeByMetadataNameInTargetAssembly(WellKnownTypeData.WellKnownTypeNames[index]);
- if (result == null)
+
+ if (result == null && throwOnNotFound)
{
throw new InvalidOperationException($"Failed to resolve well-known type '{WellKnownTypeData.WellKnownTypeNames[index]}'.");
}
+ else
+ {
+ result ??= _compilation.GetTypeByMetadataName(typeof(MissingType).FullName!)!;
+ }
+
Interlocked.CompareExchange(ref _lazyWellKnownTypes[index], result, null);
// GetTypeByMetadataName should always return the same instance for a name.
@@ -159,4 +186,6 @@ public static bool Implements(ITypeSymbol? type, ITypeSymbol interfaceType)
}
return false;
}
+
+ internal class MissingType { }
}
diff --git a/src/Validation/gen/Extensions/ITypeSymbolExtensions.cs b/src/Validation/gen/Extensions/ITypeSymbolExtensions.cs
index 3532dbd69ebc..df12e81cc6e0 100644
--- a/src/Validation/gen/Extensions/ITypeSymbolExtensions.cs
+++ b/src/Validation/gen/Extensions/ITypeSymbolExtensions.cs
@@ -95,13 +95,13 @@ internal static bool ImplementsInterface(this ITypeSymbol type, ITypeSymbol inte
// types themselves so we short-circuit on them.
internal static bool IsExemptType(this ITypeSymbol type, WellKnownTypes wellKnownTypes)
{
- return SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpContext))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpRequest))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpResponse))
+ return SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpContext))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpRequest))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_HttpResponse))
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Threading_CancellationToken))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormCollection))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection))
- || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFile))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormCollection))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection))
+ || SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.GetOptional(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_IFormFile))
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_IO_Stream))
|| SymbolEqualityComparer.Default.Equals(type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_IO_Pipelines_PipeReader));
}
diff --git a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs
index cc0624fdfb93..71466a8a547b 100644
--- a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs
+++ b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs
@@ -27,9 +27,9 @@ internal ImmutableArray ExtractValidatableTypes(IInvocationOper
? method.Parameters
: [];
- var fromServiceMetadataSymbol = wellKnownTypes.Get(
+ var fromServiceMetadataSymbol = wellKnownTypes.GetOptional(
WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromServiceMetadata);
- var fromKeyedServiceAttributeSymbol = wellKnownTypes.Get(
+ var fromKeyedServiceAttributeSymbol = wellKnownTypes.GetOptional(
WellKnownTypeData.WellKnownType.Microsoft_Extensions_DependencyInjection_FromKeyedServicesAttribute);
var skipValidationAttributeSymbol = wellKnownTypes.Get(
WellKnownTypeData.WellKnownType.Microsoft_Extensions_Validation_SkipValidationAttribute);
@@ -127,9 +127,9 @@ internal ImmutableArray ExtractValidatableMembers(ITypeSymb
var members = new List();
var resolvedRecordProperty = new List();
- var fromServiceMetadataSymbol = wellKnownTypes.Get(
+ var fromServiceMetadataSymbol = wellKnownTypes.GetOptional(
WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromServiceMetadata);
- var fromKeyedServiceAttributeSymbol = wellKnownTypes.Get(
+ var fromKeyedServiceAttributeSymbol = wellKnownTypes.GetOptional(
WellKnownTypeData.WellKnownType.Microsoft_Extensions_DependencyInjection_FromKeyedServicesAttribute);
var jsonIgnoreAttributeSymbol = wellKnownTypes.Get(
WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonIgnoreAttribute);
diff --git a/src/Validation/src/Microsoft.Extensions.Validation.csproj b/src/Validation/src/Microsoft.Extensions.Validation.csproj
index 72d50e224f42..35405452fdd3 100644
--- a/src/Validation/src/Microsoft.Extensions.Validation.csproj
+++ b/src/Validation/src/Microsoft.Extensions.Validation.csproj
@@ -11,6 +11,19 @@
true
+
+
+ false
+ Content
+ PreserveNewest
+
+
+
+
+
+
+
+