From 63330422201d00e2cf5c2df11b0e69e16917a07a Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 08:55:19 -0700 Subject: [PATCH 01/15] Add support to hook into MVC and WebApi DI frameworks This change adds support for integrating the host's dependency injection into the ASP.NET Framework entrypoints. This is done by adding a source generator that will inject the appropriate hooks so we don't have to take a dependency on the actual frameworks. A user can consume this as simply as: ``` builder.AddSystemWebDependencyInjection(); ``` and it will be hooked up to all the DI frameworks (and it's extensible for others to add more if needed with the interface IDependencyRegistrar) --- Microsoft.AspNetCore.SystemWebAdapters.sln | 21 +++ .../AppConfigFramework/Global.asax.cs | 2 +- .../Global.asax.cs | 2 +- .../AuthRemoteIdentityFramework.csproj | 4 + .../Global.asax.cs | 5 +- samples/Directory.Build.props | 16 ++- src/Directory.Packages.props | 3 + .../FrameworkDependencyInjectionGenerator.cs | 128 ++++++++++++++++++ .../IsExternalInit.cs | 5 + ....SystemWebAdapters.Analyzers.CSharp.csproj | 20 +++ .../Resolvers/Mvc.cs | 88 ++++++++++++ .../Resolvers/WebApi.cs | 90 ++++++++++++ ...SystemWebAdapters.Analyzers.Package.csproj | 27 ++++ .../CompilationExtensions.cs | 20 +++ .../HttpContextDependencyAnalyzer.cs | 55 ++++++++ ...NetCore.SystemWebAdapters.Analyzers.csproj | 20 +++ .../Hosting/IDependencyRegistrar.cs | 13 ++ .../WebObjectActivatorExtensions.cs | 87 +++++++++--- .../Analyzers.targets | 25 ++++ .../Generated/ExcludedApis.txt | 1 + .../HttpContextRequestServicesExtensions.cs | 111 +++++++++++++++ .../HttpContextWrapper.cs | 2 + ...rosoft.AspNetCore.SystemWebAdapters.csproj | 6 +- .../HttpContextTests.cs | 23 ++++ 24 files changed, 751 insertions(+), 23 deletions(-) create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/IsExternalInit.cs create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/CompilationExtensions.cs create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.csproj create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/Hosting/IDependencyRegistrar.cs create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters/Analyzers.targets create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextRequestServicesExtensions.cs diff --git a/Microsoft.AspNetCore.SystemWebAdapters.sln b/Microsoft.AspNetCore.SystemWebAdapters.sln index 70492ecd1f..4232dd5593 100644 --- a/Microsoft.AspNetCore.SystemWebAdapters.sln +++ b/Microsoft.AspNetCore.SystemWebAdapters.sln @@ -124,6 +124,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppConfigFramework", "sampl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppConfigCore", "samples\AppConfig\AppConfigCore\AppConfigCore.csproj", "{6ECA1F1C-214B-BB97-27D7-319C7F1AD90F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SystemWebAdapters.Analyzers", "src\Microsoft.AspNetCore.SystemWebAdapters.Analyzers\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.csproj", "{7A244BE9-B12E-424F-A1F1-E386C43BF0A9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp", "src\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj", "{904FF6F2-0322-9B85-7FBA-C512196D72C2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package", "src\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj", "{C46D1D06-58B3-4414-B128-82E728F3A264}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -278,6 +284,18 @@ Global {6ECA1F1C-214B-BB97-27D7-319C7F1AD90F}.Debug|Any CPU.Build.0 = Debug|Any CPU {6ECA1F1C-214B-BB97-27D7-319C7F1AD90F}.Release|Any CPU.ActiveCfg = Release|Any CPU {6ECA1F1C-214B-BB97-27D7-319C7F1AD90F}.Release|Any CPU.Build.0 = Release|Any CPU + {7A244BE9-B12E-424F-A1F1-E386C43BF0A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A244BE9-B12E-424F-A1F1-E386C43BF0A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A244BE9-B12E-424F-A1F1-E386C43BF0A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A244BE9-B12E-424F-A1F1-E386C43BF0A9}.Release|Any CPU.Build.0 = Release|Any CPU + {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Release|Any CPU.Build.0 = Release|Any CPU + {C46D1D06-58B3-4414-B128-82E728F3A264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C46D1D06-58B3-4414-B128-82E728F3A264}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C46D1D06-58B3-4414-B128-82E728F3A264}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C46D1D06-58B3-4414-B128-82E728F3A264}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -328,6 +346,9 @@ Global {16A9239C-ADC1-B9FA-8C50-081E2A420B5B} = {012BEA62-0FF1-472E-AD5F-0D2029336701} {90FE21BE-F292-A973-D281-9D4DC81CEB6F} = {012BEA62-0FF1-472E-AD5F-0D2029336701} {6ECA1F1C-214B-BB97-27D7-319C7F1AD90F} = {012BEA62-0FF1-472E-AD5F-0D2029336701} + {7A244BE9-B12E-424F-A1F1-E386C43BF0A9} = {F9DB9323-C919-49E8-8F96-B923D2F42E60} + {904FF6F2-0322-9B85-7FBA-C512196D72C2} = {F9DB9323-C919-49E8-8F96-B923D2F42E60} + {C46D1D06-58B3-4414-B128-82E728F3A264} = {F9DB9323-C919-49E8-8F96-B923D2F42E60} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DABA3C65-9D74-4EB6-9B1C-730328710EAD} diff --git a/samples/AppConfig/AppConfigFramework/Global.asax.cs b/samples/AppConfig/AppConfigFramework/Global.asax.cs index c1b1769399..634fe54b96 100644 --- a/samples/AppConfig/AppConfigFramework/Global.asax.cs +++ b/samples/AppConfig/AppConfigFramework/Global.asax.cs @@ -11,7 +11,7 @@ protected void Application_Start() HttpApplicationHost.RegisterHost(builder => { builder.AddServiceDefaults(); - builder.RegisterWebObjectActivator(); + builder.AddSystemWebDependencyInjection(); }); } } diff --git a/samples/AuthRemoteFormsAuth/AuthRemoteFormsAuthFramework/Global.asax.cs b/samples/AuthRemoteFormsAuth/AuthRemoteFormsAuthFramework/Global.asax.cs index 6c52d99eb0..3ee0c3081e 100644 --- a/samples/AuthRemoteFormsAuth/AuthRemoteFormsAuthFramework/Global.asax.cs +++ b/samples/AuthRemoteFormsAuth/AuthRemoteFormsAuthFramework/Global.asax.cs @@ -17,7 +17,7 @@ protected void Application_Start() HttpApplicationHost.RegisterHost(builder => { builder.AddServiceDefaults(); - builder.RegisterWebObjectActivator(); + builder.AddSystemWebDependencyInjection(); builder.AddSystemWebAdapters() .AddVirtualizedContentDirectories(); diff --git a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/AuthRemoteIdentityFramework.csproj b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/AuthRemoteIdentityFramework.csproj index 9d85d5c501..784fa82cea 100644 --- a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/AuthRemoteIdentityFramework.csproj +++ b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/AuthRemoteIdentityFramework.csproj @@ -1,6 +1,10 @@  net481 + + + 7.3 + disable diff --git a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Global.asax.cs b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Global.asax.cs index cc0f188d32..7167e37199 100644 --- a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Global.asax.cs +++ b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Global.asax.cs @@ -1,11 +1,9 @@ -using System.Configuration; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using Microsoft.AspNetCore.SystemWebAdapters.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; namespace MvcApp @@ -17,7 +15,7 @@ protected void Application_Start() HttpApplicationHost.RegisterHost(builder => { builder.AddServiceDefaults(); - builder.RegisterWebObjectActivator(); + builder.AddSystemWebDependencyInjection(); builder.AddSystemWebAdapters() .AddVirtualizedContentDirectories(); @@ -31,3 +29,4 @@ protected void Application_Start() } } } + diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props index 9a8659ad18..d33706a0b6 100644 --- a/samples/Directory.Build.props +++ b/samples/Directory.Build.props @@ -21,7 +21,7 @@ ASPIRE004;$(NoWarn) - + $(NoWarn);NU1507;NU1902 @@ -61,4 +61,18 @@ IDE1006; + + + + + + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 3c218d91d0..7d1fb2732e 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -5,6 +5,9 @@ + + + diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs new file mode 100644 index 0000000000..f7c3bc9db1 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs @@ -0,0 +1,128 @@ +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.AspNetCore.SystemWebAdapters.Analyzers; + +/// +/// A source generator that generates dependency injection registration code for ASP.NET Framework including: +/// +/// - ASP.NET MVC4 +/// - ASP.NET Web API 2 +/// +/// We're using a source generator here to avoid taking hard dependencies on these frameworks or releasing separate +/// packages for each framework. The generated code will only be included in the compilation if the user is +/// referencing the relevant assemblies. +/// +/// For each framework, we generate a new extension method `Add{Framework}DependencyInjection` that adds the relevant services +/// to the DI container. +/// +/// We also generate a `AddSystemWebDependencyInjection` method that calls each of the individual framework methods for whatever +/// frameworks are referenced and will be automatically updated. +/// +[Generator] +public class FrameworkDependencyInjectionGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var usedFrameworks = context.CompilationProvider.Select((compilation, token) => new FrameworksUsed + { + HttpApplicationHost = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.SystemWebAdapters.Hosting.IDependencyRegistrar") is { }, + WebApi = compilation.GetTypeByMetadataName("System.Web.Http.Dependencies.IDependencyResolver") is { }, + Mvc = compilation.GetTypeByMetadataName("System.Web.Mvc.IDependencyResolver") is { }, + }); + + context.RegisterSourceOutput(usedFrameworks, (context, frameworks) => + { + // No need to do anything if they're not referencing SystemWebAdapters with the ASP.NET Framework hosting infrastructure + if (!frameworks.HttpApplicationHost) + { + return; + } + + var source = CreateSource(frameworks.GetRegistrars()); + context.AddSource("DependencyInjectionServiceCollectionExtensions.g.cs", source); + + foreach (var registrarSource in frameworks.GetRegistrars()) + { + using var stream = typeof(FrameworkDependencyInjectionGenerator).Assembly + .GetManifestResourceStream($"Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.Resolvers.{registrarSource}.cs"); + + if (stream is null) + { + context.AddSource($"{registrarSource}.g.cs", $"// Could not find resource for {registrarSource}"); + } + else + { + context.AddSource($"{registrarSource}.g.cs", SourceText.From(stream, canBeEmbedded: true)); + } + } + }); + } + + + private sealed record class FrameworksUsed + { + public bool HttpApplicationHost { get; init; } + + public bool Mvc { get; init; } + + public bool WebApi { get; init; } + + public IEnumerable GetRegistrars() + { + if (Mvc) + { + yield return "Mvc"; + } + + if (WebApi) + { + yield return "WebApi"; + } + } + } + private static string CreateSource(IEnumerable registrars) + { + using var writer = new StringWriter(); + using var indentedWriter = new IndentedTextWriter(writer); + + indentedWriter.WriteLine("// "); + indentedWriter.WriteLine(); + indentedWriter.WriteLine("using System;"); + indentedWriter.WriteLine("using System.Collections.Generic;"); + indentedWriter.WriteLine("using Microsoft.Extensions.DependencyInjection;"); + indentedWriter.WriteLine("using Microsoft.Extensions.DependencyInjection.Extensions;"); + indentedWriter.WriteLine("using Microsoft.AspNetCore.SystemWebAdapters.Hosting;"); + indentedWriter.WriteLine(); + indentedWriter.WriteLine("namespace System.Web"); + indentedWriter.WriteLine("{"); + indentedWriter.Indent++; + + indentedWriter.WriteLine("internal static partial class SystemWebFrameworksDependencyInjectionServiceCollectionExtensions"); + indentedWriter.WriteLine("{"); + indentedWriter.Indent++; + + indentedWriter.WriteLine("public static void AddSystemWebDependencyInjection(this HttpApplicationHostBuilder builder)"); + indentedWriter.WriteLine("{"); + indentedWriter.Indent++; + indentedWriter.WriteLine("builder.AddWebObjectActivator();"); + foreach (var registrar in registrars) + { + indentedWriter.WriteLine($"builder.Add{registrar}DependencyInjection();"); + } + indentedWriter.Indent--; + indentedWriter.WriteLine("}"); + + indentedWriter.Indent--; + indentedWriter.WriteLine("}"); + indentedWriter.Indent--; + indentedWriter.WriteLine("}"); + + return writer.ToString(); + } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/IsExternalInit.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/IsExternalInit.cs new file mode 100644 index 0000000000..4893959d19 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/IsExternalInit.cs @@ -0,0 +1,5 @@ +namespace System.Runtime.CompilerServices +{ + // This class is required for C# 9.0 init-only properties in older frameworks + internal static class IsExternalInit { } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj new file mode 100644 index 0000000000..10bf320664 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + enable + false + true + true + + + + + + + + + + + + diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs new file mode 100644 index 0000000000..8a8f6ceb0a --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs @@ -0,0 +1,88 @@ +// + +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.AspNetCore.SystemWebAdapters.Hosting; +using System.Web.Mvc; + +namespace System.Web +{ + internal static partial class SystemWebFrameworksDependencyInjectionServiceCollectionExtensions + { + public static void AddMvcDependencyInjection(this HttpApplicationHostBuilder builder) + { + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + builder.Services.TryAddSingleton(); + } + + private sealed class MvcAdapterDependencyResolver : IDependencyRegistrar, IDependencyResolver, IViewPageActivator, IDisposable + { + private readonly IServiceProvider _serviceProvider; + + public MvcAdapterDependencyResolver(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public string Name => "System.Web.Mvc.DependencyResolver"; + + public bool IsActive => object.ReferenceEquals(DependencyResolver.Current, this); + + public bool Enable(bool force) + { + if (force || !IsActive) + { + DependencyResolver.SetResolver(this); + return true; + } + + return false; + } + + object IDependencyResolver.GetService(Type serviceType) + { + return GetInternalService(_serviceProvider, serviceType); + } + + IEnumerable IDependencyResolver.GetServices(Type serviceType) + { + var genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType); + return (IEnumerable)GetInternalService(_serviceProvider, genericEnumerable); + } + + object IViewPageActivator.Create(ControllerContext controllerContext, Type type) + { + var services = controllerContext.HttpContext.GetRequestServices(); + + return GetInternalService(services, type); + } + + private object GetInternalService(IServiceProvider provider, Type serviceType) + { + var existing = provider.GetService(serviceType); + + if (existing != null) + { + return existing; + } + + if (serviceType.IsInterface || serviceType.IsAbstract) + { + return null; + } + + return ActivatorUtilities.CreateInstance(provider, serviceType); + } + + void IDisposable.Dispose() + { + if (IsActive) + { + DependencyResolver.SetResolver(null); + } + } + } + } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs new file mode 100644 index 0000000000..54b57aea6d --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs @@ -0,0 +1,90 @@ +// + +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.AspNetCore.SystemWebAdapters.Hosting; +using System.Web.Http; +using System.Web.Http.Dependencies; + +namespace System.Web +{ + internal static partial class SystemWebFrameworksDependencyInjectionServiceCollectionExtensions + { + public static void AddWebApiDependencyInjection(this HttpApplicationHostBuilder builder) + { + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + } + + private sealed class WebApiAdapterDependencyResolver : IDependencyRegistrar, IDependencyResolver, IDisposable + { + private readonly IServiceProvider _services; + + public WebApiAdapterDependencyResolver(IServiceProvider services) + { + _services = services; + } + + public string Name => "GlobalConfiguration.Configuration.DependencyResolver"; + + public bool IsActive => ReferenceEquals(GlobalConfiguration.Configuration.DependencyResolver, this); + + public bool Enable(bool force) + { + if (force || !IsActive) + { + GlobalConfiguration.Configuration.DependencyResolver = this; + return true; + } + + return false; + } + + public IDependencyScope BeginScope() + { + return new Scope(_services.CreateScope()); + } + + public object GetService(Type serviceType) + { + return _services.GetService(serviceType); + } + + public IEnumerable GetServices(Type serviceType) + { + return _services.GetServices(serviceType); + } + + void IDisposable.Dispose() + { + // Cannot set the dependency resolver to null as it throws + } + + private sealed class Scope : IDependencyScope + { + private readonly IServiceScope _scope; + + public Scope(IServiceScope scope) + { + _scope = scope; + } + + void IDisposable.Dispose() + { + _scope.Dispose(); + } + + object IDependencyScope.GetService(Type serviceType) + { + return _scope.ServiceProvider.GetService(serviceType); + } + + IEnumerable IDependencyScope.GetServices(Type serviceType) + { + return _scope.ServiceProvider.GetServices(serviceType); + } + } + } + } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj new file mode 100644 index 0000000000..516dc5bf11 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj @@ -0,0 +1,27 @@ + + + + netstandard2.0 + Microsoft.AspNetCore.SystemWebAdapters.Analyzers + false + true + true + true + true + + $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput + + + + + + + + + + + + + + + diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/CompilationExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/CompilationExtensions.cs new file mode 100644 index 0000000000..81ffc9ed4a --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/CompilationExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Microsoft.AspNetCore.SystemWebAdapters.Analyzers; + +internal static class CompilationExtensions +{ + public static bool IsInAssembly(this ITypeSymbol type, string assemblyName) + { + return string.Equals(assemblyName, type.ContainingAssembly?.Name, StringComparison.Ordinal); + } + + public static bool IsType(this Compilation compilation, ITypeSymbol type, string name) + { + return compilation.GetTypeByMetadataName(name) is { } targetType + && SymbolEqualityComparer.Default.Equals(type, targetType); + } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs new file mode 100644 index 0000000000..c55dfee712 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.AspNetCore.SystemWebAdapters.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class HttpContextDependencyAnalyzer : DiagnosticAnalyzer +{ + private static DiagnosticDescriptor s_Rule = new DiagnosticDescriptor( + id: "SYSWEB001", + title: "Don't use System.Web.HttpContext.GetServices", + messageFormat: "System.Web.HttpContext.GetServices is not extensible. Prefer System.Web.HttpContext.GetRequestServices() instead", + category: "Error", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => [s_Rule]; + + public override void Initialize(AnalysisContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + context.EnableConcurrentExecution(); + context.RegisterOperationAction(context => + { + if (context.Operation is not IConversionOperation { Type: { } type, Operand.Type: { } operand }) + { + return; + } + + // Allows us to fail fast for types we don't care about + if (!operand.IsInAssembly("System.Web")) + { + return; + } + + if (!context.Compilation.IsType(type, "System.IServiceProvider")) + { + return; + } + + if (context.Compilation.IsType(operand, "System.Web.HttpContext") || context.Compilation.IsType(operand, "System.Web.HttpContextBase")) + { + context.ReportDiagnostic(Diagnostic.Create(s_Rule, context.Operation.Syntax.GetLocation())); + } + }, OperationKind.Conversion); + } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.csproj b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.csproj new file mode 100644 index 0000000000..0dcfd290e6 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + enable + false + true + true + + + + + + + + + + + + diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/Hosting/IDependencyRegistrar.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/Hosting/IDependencyRegistrar.cs new file mode 100644 index 0000000000..1f3ba85089 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/Hosting/IDependencyRegistrar.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.SystemWebAdapters.Hosting; + +public interface IDependencyRegistrar : IDisposable +{ + string Name { get; } + + bool IsActive { get; } + + bool Enable(bool force); +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/WebObjectActivatorExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/WebObjectActivatorExtensions.cs index 2a82764037..36fe19b81d 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/WebObjectActivatorExtensions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/WebObjectActivatorExtensions.cs @@ -1,9 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.ComponentModel; using System.Reflection; using Microsoft.AspNetCore.SystemWebAdapters.Hosting; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -11,30 +13,59 @@ namespace System.Web; public static partial class WebObjectActivatorExtensions { + [Obsolete("Use AddWebObjectActivator instead or use .AddSystemWebDependencyInjection() to configure all containers in your application.")] + [EditorBrowsable(EditorBrowsableState.Never)] public static HttpApplicationHostBuilder RegisterWebObjectActivator(this HttpApplicationHostBuilder builder) + => builder.AddWebObjectActivator(); + + public static HttpApplicationHostBuilder AddWebObjectActivator(this HttpApplicationHostBuilder builder) { if (builder is null) { throw new ArgumentNullException(nameof(builder)); } - builder.Services.AddHostedService(); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + builder.Services.TryAddSingleton(); + builder.Services.AddHostedService(); return builder; } - private sealed partial class WebObjectActivatorHostServices : IServiceProvider, IHostedService + private sealed partial class WebObjectRegistrar : IDependencyRegistrar, IServiceProvider { - private const string ErrorMessage = "WebObjectActivator is already set and will not be overridden"; - private readonly IServiceProvider _services; - public WebObjectActivatorHostServices(IServiceProvider services) + public WebObjectRegistrar(IServiceProvider _services) { - _services = services; + this._services = _services; + } - public object? GetService(Type serviceType) + public string Name => nameof(HttpRuntime.WebObjectActivator); + + public bool IsActive => ReferenceEquals(HttpRuntime.WebObjectActivator, this); + + public void Dispose() + { + if (IsActive) + { + HttpRuntime.WebObjectActivator = null; + } + } + + public bool Enable(bool force) + { + if (force || !IsActive) + { + HttpRuntime.WebObjectActivator = this; + return true; + } + + return false; + } + + object? IServiceProvider.GetService(Type serviceType) { if (serviceType == typeof(IServiceProvider)) { @@ -64,27 +95,51 @@ static object CreateNonPublicInstance(Type serviceType) => Activator.CreateInsta null, null); } + } + + private sealed class ScopedServiceProviderFactory(IServiceProvider services) : IServiceScopeFactoryProxy + { + [Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Handled by caller")] + public IServiceScopeProxy CreateScope() => new ScopedServiceProvider(services.CreateScope()); + + private sealed class ScopedServiceProvider(IServiceScope scope) : IServiceScopeProxy + { + public IServiceProvider ServiceProvider => scope.ServiceProvider; + + public void Dispose() => scope.Dispose(); + } + } + + private sealed partial class DependencyRegistrarActivator(ILogger logger, IEnumerable registrars) : IHostedService + { + [LoggerMessage(LogLevel.Critical, "{Resolver} was not configured. Most likely it had already been set.")] + private static partial void LogResolverNotConfigured(ILogger logger, string resolver); + + [LoggerMessage(LogLevel.Trace, "{Resolver} was configured for dependency injection.")] + private static partial void LogResolverConfigured(ILogger logger, string resolver); Task IHostedService.StartAsync(CancellationToken cancellationToken) { - if (HttpRuntime.WebObjectActivator is { }) + foreach (var registrar in registrars) { - LogAlreadySet(_services.GetRequiredService>()); - throw new InvalidOperationException(ErrorMessage); + registrar.Enable(force: false); + + if (registrar.IsActive) + { + LogResolverConfigured(logger, registrar.Name); + } + else + { + LogResolverNotConfigured(logger, registrar.Name); + } } - - HttpRuntime.WebObjectActivator = this; return Task.CompletedTask; } Task IHostedService.StopAsync(CancellationToken cancellationToken) { - HttpRuntime.WebObjectActivator = null; return Task.CompletedTask; } - - [LoggerMessage(LogLevel.Critical, ErrorMessage)] - private static partial void LogAlreadySet(ILogger logger); } } diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Analyzers.targets b/src/Microsoft.AspNetCore.SystemWebAdapters/Analyzers.targets new file mode 100644 index 0000000000..578b7ee1fe --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Analyzers.targets @@ -0,0 +1,25 @@ + + + $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/ExcludedApis.txt b/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/ExcludedApis.txt index 340b918113..52f9e216fe 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/ExcludedApis.txt +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/ExcludedApis.txt @@ -28,6 +28,7 @@ T:System.Web.HtmlString T:Microsoft.AspNetCore.SystemWebAdapters.ITraceContext T:Microsoft.AspNetCore.SystemWebAdapters.AppConfiguration +T:System.Web.HttpContextRequestServicesExtensions # Only available .NET 4.7.2+ P:System.Web.HttpRuntime.WebObjectActivator diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextRequestServicesExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextRequestServicesExtensions.cs new file mode 100644 index 0000000000..72895cecaa --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextRequestServicesExtensions.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SystemWebAdapters; + +namespace System.Web; + +#if NETFRAMEWORK +/// +/// This is used instead of the standard IServiceScopeFactory to avoid taking a dependency on Microsoft.Extensions.DependencyInjection.Abstractions +/// +internal interface IServiceScopeFactoryProxy +{ + IServiceScopeProxy CreateScope(); +} + +/// +/// This is used instead of the standard IServiceScope to avoid taking a dependency on Microsoft.Extensions.DependencyInjection.Abstractions +/// +internal interface IServiceScopeProxy : IDisposable +{ + IServiceProvider ServiceProvider { get; } +} +#endif + +[Diagnostics.CodeAnalysis.SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "")] +public static class HttpContextRequestServicesExtensions +{ + public static IServiceProvider GetRequestServices(this HttpContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + +#if NET + return context.AsAspNetCore().RequestServices; +#elif NETSTANDARD + throw new PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web"); +#else + var sp = GetServiceProviderInternal(context.Items, out var scope); + + if (scope is { }) + { + context.DisposeOnPipelineCompleted(scope); + } + + return sp; +#endif + } + + public static IServiceProvider GetRequestServices(this HttpContextBase context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + +#if NET + if (context is HttpContextWrapper wrapper) + { + return wrapper.InnerContext.AsAspNetCore().RequestServices; + } + + return context.GetService(typeof(IServiceProvider)) as IServiceProvider ?? throw new InvalidOperationException("Could not get a service provider for the current request"); +#elif NETSTANDARD + throw new PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web"); +#else + var sp = GetServiceProviderInternal(context.Items, out var scope); + + if (scope is { }) + { + context.DisposeOnPipelineCompleted(scope); + } + + return sp; +#endif + } + +#if NETFRAMEWORK + private const string RequestServicesKey = "__RequestServices"; + + private static IServiceProvider GetServiceProviderInternal(IDictionary items, out IServiceScopeProxy? scoped) + { + if (items[RequestServicesKey] is IServiceProvider services) + { + scoped = null; + return services; + } + + var scope = HttpRuntime.WebObjectActivator?.GetService(typeof(IServiceScopeFactoryProxy)) as IServiceScopeFactoryProxy; + + if (scope is null) + { + throw new InvalidOperationException("Could not retrieve service to get scoped services."); + } + + scoped = scope.CreateScope(); + + items[RequestServicesKey] = scoped.ServiceProvider; + + return scoped.ServiceProvider; + } +#endif +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextWrapper.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextWrapper.cs index 26a73b8503..bd7c89f10c 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextWrapper.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextWrapper.cs @@ -23,6 +23,8 @@ public HttpContextWrapper(HttpContext httpContext) _context = httpContext; } + internal HttpContext InnerContext => _context; + public override DateTime Timestamp => _context.Timestamp; public override IDictionary Items => _context.Items; diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Microsoft.AspNetCore.SystemWebAdapters.csproj b/src/Microsoft.AspNetCore.SystemWebAdapters/Microsoft.AspNetCore.SystemWebAdapters.csproj index f110924194..44038002af 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters/Microsoft.AspNetCore.SystemWebAdapters.csproj +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Microsoft.AspNetCore.SystemWebAdapters.csproj @@ -1,4 +1,4 @@ - + net8.0;netstandard2.0;net45;net472 @@ -34,6 +34,7 @@ + @@ -56,6 +57,8 @@ + + @@ -72,6 +75,7 @@ + diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs index 4895ed53ce..74af4affde 100644 --- a/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs +++ b/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs @@ -156,6 +156,29 @@ public void GetServiceReturnsExpected() Assert.Null(provider.GetService()); } + [Fact] + public void GetRequestServiceExtensions() + { + var coreContext = new DefaultHttpContext(); + + var requestServices = coreContext.AsSystemWeb().GetRequestServices(); + + Assert.Equal(coreContext.RequestServices, requestServices); + } + + [Fact] + public void GetRequestServiceExtensionsBase() + { + var coreContext = new DefaultHttpContext(); + var sp = new Mock(); + sp.Setup(s => s.GetService(typeof(IServiceProvider))).Returns(sp.Object); + coreContext.RequestServices = sp.Object; + + var requestServices = new HttpContextWrapper(coreContext.AsSystemWeb()).GetRequestServices(); + + Assert.Equal(coreContext.RequestServices, requestServices); + } + [Fact] public void DefaultItemsContains() { From d8b470aebea5990adf64bdc2ccbc3ec94af67f2a Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 08:59:45 -0700 Subject: [PATCH 02/15] fix some warnings --- .../AnalyzerReleases.Shipped.md | 3 +++ .../AnalyzerReleases.Unshipped.md | 8 ++++++++ .../HttpContextDependencyAnalyzer.cs | 4 ++-- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Shipped.md create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Shipped.md b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000..60b59dd99b --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000..26c8359286 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md @@ -0,0 +1,8 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +SYSWEB001 | Error | Error | HttpContextDependencyAnalyzer \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs index c55dfee712..f8ea8e088b 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs @@ -6,13 +6,13 @@ namespace Microsoft.AspNetCore.SystemWebAdapters.Analyzers; -[DiagnosticAnalyzer(LanguageNames.CSharp)] +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class HttpContextDependencyAnalyzer : DiagnosticAnalyzer { private static DiagnosticDescriptor s_Rule = new DiagnosticDescriptor( id: "SYSWEB001", title: "Don't use System.Web.HttpContext.GetServices", - messageFormat: "System.Web.HttpContext.GetServices is not extensible. Prefer System.Web.HttpContext.GetRequestServices() instead", + messageFormat: "System.Web.HttpContext.GetServices is not extensible. Prefer System.Web.HttpContext.GetRequestServices() instead.", category: "Error", defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); From 54c2b688b3305f1fd5ce981dee8051808b0408e5 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 10:01:01 -0700 Subject: [PATCH 03/15] add some comments and support IControllerActivator --- Microsoft.AspNetCore.SystemWebAdapters.sln | 7 ---- .../FrameworkDependencyInjectionGenerator.cs | 38 ++++++++++++++----- .../Resolvers/Mvc.cs | 24 ++++++++---- .../Resolvers/WebApi.cs | 35 +++++------------ ...SystemWebAdapters.Analyzers.Package.csproj | 27 ------------- 5 files changed, 54 insertions(+), 77 deletions(-) delete mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj diff --git a/Microsoft.AspNetCore.SystemWebAdapters.sln b/Microsoft.AspNetCore.SystemWebAdapters.sln index 4232dd5593..42b4df1c85 100644 --- a/Microsoft.AspNetCore.SystemWebAdapters.sln +++ b/Microsoft.AspNetCore.SystemWebAdapters.sln @@ -128,8 +128,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.System EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp", "src\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj", "{904FF6F2-0322-9B85-7FBA-C512196D72C2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package", "src\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj", "{C46D1D06-58B3-4414-B128-82E728F3A264}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -292,10 +290,6 @@ Global {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Release|Any CPU.Build.0 = Release|Any CPU - {C46D1D06-58B3-4414-B128-82E728F3A264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C46D1D06-58B3-4414-B128-82E728F3A264}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C46D1D06-58B3-4414-B128-82E728F3A264}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C46D1D06-58B3-4414-B128-82E728F3A264}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -348,7 +342,6 @@ Global {6ECA1F1C-214B-BB97-27D7-319C7F1AD90F} = {012BEA62-0FF1-472E-AD5F-0D2029336701} {7A244BE9-B12E-424F-A1F1-E386C43BF0A9} = {F9DB9323-C919-49E8-8F96-B923D2F42E60} {904FF6F2-0322-9B85-7FBA-C512196D72C2} = {F9DB9323-C919-49E8-8F96-B923D2F42E60} - {C46D1D06-58B3-4414-B128-82E728F3A264} = {F9DB9323-C919-49E8-8F96-B923D2F42E60} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DABA3C65-9D74-4EB6-9B1C-730328710EAD} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs index f7c3bc9db1..90c4862b37 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs @@ -44,21 +44,22 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return; } - var source = CreateSource(frameworks.GetRegistrars()); + var registrars = frameworks.GetRegistrars().ToList(); + var source = CreateSource(registrars); context.AddSource("DependencyInjectionServiceCollectionExtensions.g.cs", source); - foreach (var registrarSource in frameworks.GetRegistrars()) + foreach (var registrarSource in registrars) { using var stream = typeof(FrameworkDependencyInjectionGenerator).Assembly - .GetManifestResourceStream($"Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.Resolvers.{registrarSource}.cs"); + .GetManifestResourceStream($"Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.Resolvers.{registrarSource.Name}.cs"); if (stream is null) { - context.AddSource($"{registrarSource}.g.cs", $"// Could not find resource for {registrarSource}"); + context.AddSource($"{registrarSource.Name}.g.cs", $"// Could not find resource for {registrarSource.Name}"); } else { - context.AddSource($"{registrarSource}.g.cs", SourceText.From(stream, canBeEmbedded: true)); + context.AddSource($"{registrarSource.Name}.g.cs", SourceText.From(stream, canBeEmbedded: true)); } } }); @@ -73,20 +74,21 @@ private sealed record class FrameworksUsed public bool WebApi { get; init; } - public IEnumerable GetRegistrars() + public IEnumerable GetRegistrars() { if (Mvc) { - yield return "Mvc"; + yield return new("Mvc", "System.Web.Mvc.DependencyResolver"); } if (WebApi) { - yield return "WebApi"; + yield return new("WebApi", "System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver"); } } } - private static string CreateSource(IEnumerable registrars) + + private static string CreateSource(IReadOnlyCollection registrars) { using var writer = new StringWriter(); using var indentedWriter = new IndentedTextWriter(writer); @@ -107,13 +109,27 @@ private static string CreateSource(IEnumerable registrars) indentedWriter.WriteLine("{"); indentedWriter.Indent++; + indentedWriter.WriteLine("/// "); + indentedWriter.WriteLine("/// Adds dependency injection support for ASP.NET Framework components including:"); + + indentedWriter.WriteLine(@"/// "); + indentedWriter.Indent++; + indentedWriter.WriteLine(@"/// ASP.NET Framework Core Components: "); + foreach (var registrar in registrars) + { + indentedWriter.WriteLine($@"/// {registrar.Name}: "); + } + indentedWriter.Indent--; + indentedWriter.WriteLine("/// "); + indentedWriter.WriteLine("/// "); + indentedWriter.WriteLine("public static void AddSystemWebDependencyInjection(this HttpApplicationHostBuilder builder)"); indentedWriter.WriteLine("{"); indentedWriter.Indent++; indentedWriter.WriteLine("builder.AddWebObjectActivator();"); foreach (var registrar in registrars) { - indentedWriter.WriteLine($"builder.Add{registrar}DependencyInjection();"); + indentedWriter.WriteLine($"builder.Add{registrar.Name}DependencyInjection();"); } indentedWriter.Indent--; indentedWriter.WriteLine("}"); @@ -125,4 +141,6 @@ private static string CreateSource(IEnumerable registrars) return writer.ToString(); } + + private sealed record DependencyRegistrar(string Name, string Api); } diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs index 8a8f6ceb0a..e030205ff0 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs @@ -6,18 +6,24 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.AspNetCore.SystemWebAdapters.Hosting; using System.Web.Mvc; +using System.Web.Routing; namespace System.Web { internal static partial class SystemWebFrameworksDependencyInjectionServiceCollectionExtensions { + /// + /// Adds the MVC dependency resolver so that MVC components can resolve services from . + /// public static void AddMvcDependencyInjection(this HttpApplicationHostBuilder builder) { - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); - builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(sp => sp.GetRequiredService())); + builder.Services.TryAddSingleton(sp => sp.GetRequiredService()); + builder.Services.TryAddSingleton(sp => sp.GetRequiredService()); } - private sealed class MvcAdapterDependencyResolver : IDependencyRegistrar, IDependencyResolver, IViewPageActivator, IDisposable + private sealed class MvcAdapterDependencyResolver : IDependencyRegistrar, IDependencyResolver, IViewPageActivator, IControllerActivator, IDisposable { private readonly IServiceProvider _serviceProvider; @@ -41,10 +47,7 @@ public bool Enable(bool force) return false; } - object IDependencyResolver.GetService(Type serviceType) - { - return GetInternalService(_serviceProvider, serviceType); - } + object IDependencyResolver.GetService(Type serviceType) => GetInternalService(_serviceProvider, serviceType); IEnumerable IDependencyResolver.GetServices(Type serviceType) { @@ -55,10 +58,15 @@ IEnumerable IDependencyResolver.GetServices(Type serviceType) object IViewPageActivator.Create(ControllerContext controllerContext, Type type) { var services = controllerContext.HttpContext.GetRequestServices(); - return GetInternalService(services, type); } + IController IControllerActivator.Create(RequestContext requestContext, Type controllerType) + { + var services = requestContext.HttpContext.GetRequestServices(); + return GetInternalService(services, controllerType) as IController; + } + private object GetInternalService(IServiceProvider provider, Type serviceType) { var existing = provider.GetService(serviceType); diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs index 54b57aea6d..401259caf9 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs @@ -12,6 +12,9 @@ namespace System.Web { internal static partial class SystemWebFrameworksDependencyInjectionServiceCollectionExtensions { + /// + /// Adds the WebApi dependency resolver so that WebApi components can resolve services from . + /// public static void AddWebApiDependencyInjection(this HttpApplicationHostBuilder builder) { builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); @@ -26,7 +29,7 @@ public WebApiAdapterDependencyResolver(IServiceProvider services) _services = services; } - public string Name => "GlobalConfiguration.Configuration.DependencyResolver"; + public string Name => "System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver"; public bool IsActive => ReferenceEquals(GlobalConfiguration.Configuration.DependencyResolver, this); @@ -41,20 +44,11 @@ public bool Enable(bool force) return false; } - public IDependencyScope BeginScope() - { - return new Scope(_services.CreateScope()); - } + public IDependencyScope BeginScope() => new Scope(_services.CreateScope()); - public object GetService(Type serviceType) - { - return _services.GetService(serviceType); - } + public object GetService(Type serviceType) => _services.GetService(serviceType); - public IEnumerable GetServices(Type serviceType) - { - return _services.GetServices(serviceType); - } + public IEnumerable GetServices(Type serviceType) => _services.GetServices(serviceType); void IDisposable.Dispose() { @@ -70,20 +64,11 @@ public Scope(IServiceScope scope) _scope = scope; } - void IDisposable.Dispose() - { - _scope.Dispose(); - } + void IDisposable.Dispose() => _scope.Dispose(); - object IDependencyScope.GetService(Type serviceType) - { - return _scope.ServiceProvider.GetService(serviceType); - } + object IDependencyScope.GetService(Type serviceType) => _scope.ServiceProvider.GetService(serviceType); - IEnumerable IDependencyScope.GetServices(Type serviceType) - { - return _scope.ServiceProvider.GetServices(serviceType); - } + IEnumerable IDependencyScope.GetServices(Type serviceType) => _scope.ServiceProvider.GetServices(serviceType); } } } diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj deleted file mode 100644 index 516dc5bf11..0000000000 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.Package.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - netstandard2.0 - Microsoft.AspNetCore.SystemWebAdapters.Analyzers - false - true - true - true - true - - $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput - - - - - - - - - - - - - - - From 5c304ac354bfc3f5c6024d206ff3dddb99693b65 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 10:19:09 -0700 Subject: [PATCH 04/15] per copilot --- .../CompilationExtensions.cs | 2 -- .../HttpContextRequestServicesExtensions.cs | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/CompilationExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/CompilationExtensions.cs index 81ffc9ed4a..b2a9226a68 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/CompilationExtensions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/CompilationExtensions.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.SystemWebAdapters.Analyzers; diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextRequestServicesExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextRequestServicesExtensions.cs index 72895cecaa..d152952eba 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextRequestServicesExtensions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextRequestServicesExtensions.cs @@ -1,12 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.AspNetCore.SystemWebAdapters; namespace System.Web; From fde3042b14fe0fa048bdf15727b2ca599aaa618c Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 11:06:37 -0700 Subject: [PATCH 05/15] rename description --- .../AnalyzerReleases.Unshipped.md | 2 +- .../HttpContextDependencyAnalyzer.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md index 26c8359286..f94f33d9c4 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md @@ -5,4 +5,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- -SYSWEB001 | Error | Error | HttpContextDependencyAnalyzer \ No newline at end of file +SYSWEB1001 | Error | Error | HttpContextDependencyAnalyzer \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs index f8ea8e088b..05009e9454 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs @@ -10,9 +10,9 @@ namespace Microsoft.AspNetCore.SystemWebAdapters.Analyzers; public class HttpContextDependencyAnalyzer : DiagnosticAnalyzer { private static DiagnosticDescriptor s_Rule = new DiagnosticDescriptor( - id: "SYSWEB001", - title: "Don't use System.Web.HttpContext.GetServices", - messageFormat: "System.Web.HttpContext.GetServices is not extensible. Prefer System.Web.HttpContext.GetRequestServices() instead.", + id: "SYSWEB1001", + title: "Do not cast HttpContext or HttpContextBase to IServiceProvider", + messageFormat: "{0} is implicitly convertable to IServiceProvider but does not return any useful services. Prefer the {0}.GetRequestServices() extension method instead.", category: "Error", defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -48,7 +48,7 @@ public override void Initialize(AnalysisContext context) if (context.Compilation.IsType(operand, "System.Web.HttpContext") || context.Compilation.IsType(operand, "System.Web.HttpContextBase")) { - context.ReportDiagnostic(Diagnostic.Create(s_Rule, context.Operation.Syntax.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(s_Rule, context.Operation.Syntax.GetLocation(), operand.Name)); } }, OperationKind.Conversion); } From 19b8601605cddec13da5c87f6bf1307c2ab0466c Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 11:30:42 -0700 Subject: [PATCH 06/15] use 4.11 of codeanalysis --- .../Controllers/HomeController.cs | 3 +++ src/Directory.Packages.props | 7 +++---- ...t.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj | 1 - .../Properties/launchSettings.json | 8 ++++++++ .../AnalyzerReleases.Unshipped.md | 2 +- .../HttpContextDependencyAnalyzer.cs | 2 +- ...icrosoft.AspNetCore.SystemWebAdapters.Analyzers.csproj | 1 - .../Properties/launchSettings.json | 8 ++++++++ 8 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Properties/launchSettings.json create mode 100644 src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Properties/launchSettings.json diff --git a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs index 0cf1dde158..d5bda0269c 100644 --- a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs +++ b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs @@ -1,4 +1,5 @@ using System.Web.Mvc; +using Microsoft.Extensions.DependencyInjection; namespace MvcApp.Controllers { @@ -6,6 +7,8 @@ public class HomeController : Controller { public ActionResult Index() { + HttpContext.GetRequiredService(); + System.Web.HttpContext.Current.GetRequiredService(); Session.Add("test-value", 5); return View(); } diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 7d1fb2732e..6512173adf 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -5,9 +5,8 @@ - - - + + @@ -20,4 +19,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj index 10bf320664..710c6bb674 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj @@ -9,7 +9,6 @@ - diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Properties/launchSettings.json b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Properties/launchSettings.json new file mode 100644 index 0000000000..883516f680 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "AuthRemoteIdentityFramework": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\..\\samples\\AuthRemoteIdentity\\AuthRemoteIdentityFramework\\AuthRemoteIdentityFramework.csproj" + } + } +} diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md index f94f33d9c4..101129afc4 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md @@ -5,4 +5,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- -SYSWEB1001 | Error | Error | HttpContextDependencyAnalyzer \ No newline at end of file +SYSWEB2001 | Error | Error | HttpContextDependencyAnalyzer diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs index 05009e9454..d3d6cbb8f4 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.SystemWebAdapters.Analyzers; public class HttpContextDependencyAnalyzer : DiagnosticAnalyzer { private static DiagnosticDescriptor s_Rule = new DiagnosticDescriptor( - id: "SYSWEB1001", + id: "SYSWEB2001", title: "Do not cast HttpContext or HttpContextBase to IServiceProvider", messageFormat: "{0} is implicitly convertable to IServiceProvider but does not return any useful services. Prefer the {0}.GetRequestServices() extension method instead.", category: "Error", diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.csproj b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.csproj index 0dcfd290e6..8b92f68aef 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.csproj +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.csproj @@ -9,7 +9,6 @@ - diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Properties/launchSettings.json b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Properties/launchSettings.json new file mode 100644 index 0000000000..c9ce8d3d81 --- /dev/null +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "AuthRemoteIdentityFramework": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\..\\samples\\AuthRemoteIdentity\\AuthRemoteIdentityFramework\\AuthRemoteIdentityFramework.csproj" + } + } +} \ No newline at end of file From 4b7beba9abb0d1713d78a7a6ef5fafe29049ce2c Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 12:28:47 -0700 Subject: [PATCH 07/15] change category to Usage --- .../AnalyzerReleases.Unshipped.md | 2 +- .../HttpContextDependencyAnalyzer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md index 101129afc4..5af8be603e 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/AnalyzerReleases.Unshipped.md @@ -5,4 +5,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- -SYSWEB2001 | Error | Error | HttpContextDependencyAnalyzer +SYSWEB2001 | Usage | Error | HttpContextDependencyAnalyzer diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs index d3d6cbb8f4..8bd3c136a9 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers/HttpContextDependencyAnalyzer.cs @@ -13,7 +13,7 @@ public class HttpContextDependencyAnalyzer : DiagnosticAnalyzer id: "SYSWEB2001", title: "Do not cast HttpContext or HttpContextBase to IServiceProvider", messageFormat: "{0} is implicitly convertable to IServiceProvider but does not return any useful services. Prefer the {0}.GetRequestServices() extension method instead.", - category: "Error", + category: "Usage", defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); From b5ecb737f87261811728d7a98fb68adcc438fedc Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 14:43:04 -0700 Subject: [PATCH 08/15] fix break --- .../AuthRemoteIdentityFramework/Controllers/HomeController.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs index d5bda0269c..196229270d 100644 --- a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs +++ b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs @@ -7,8 +7,6 @@ public class HomeController : Controller { public ActionResult Index() { - HttpContext.GetRequiredService(); - System.Web.HttpContext.Current.GetRequiredService(); Session.Add("test-value", 5); return View(); } From 0f21eb984e7f8804426257f6ab359eed5de33976 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 14:43:20 -0700 Subject: [PATCH 09/15] remove using --- .../AuthRemoteIdentityFramework/Controllers/HomeController.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs index 196229270d..0cf1dde158 100644 --- a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs +++ b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Controllers/HomeController.cs @@ -1,5 +1,4 @@ using System.Web.Mvc; -using Microsoft.Extensions.DependencyInjection; namespace MvcApp.Controllers { From 274df04fc13375f1f2897b35e990ba8516f8e1bd Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 22 Oct 2025 14:53:01 -0700 Subject: [PATCH 10/15] update tests --- .../HttpContextTests.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs index 74af4affde..9dfc6ab15b 100644 --- a/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs +++ b/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs @@ -167,18 +167,23 @@ public void GetRequestServiceExtensions() } [Fact] - public void GetRequestServiceExtensionsBase() + public void GetRequestServiceExtensionsWrapper() { var coreContext = new DefaultHttpContext(); - var sp = new Mock(); - sp.Setup(s => s.GetService(typeof(IServiceProvider))).Returns(sp.Object); - coreContext.RequestServices = sp.Object; - var requestServices = new HttpContextWrapper(coreContext.AsSystemWeb()).GetRequestServices(); Assert.Equal(coreContext.RequestServices, requestServices); } + [Fact] + public void GetRequestServiceExtensionsBaseNoService() + { + var coreContext = new DefaultHttpContext(); + var contextBase = new Mock(); + + Assert.Throws(() => contextBase.Object.GetRequestServices()); + } + [Fact] public void DefaultItemsContains() { From a820175487d766e0ac2eb7a7cb081e6fc564d906 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Thu, 23 Oct 2025 17:43:55 -0700 Subject: [PATCH 11/15] add di e2e tests --- Microsoft.AspNetCore.SystemWebAdapters.sln | 21 +- .../DependencyInjectionAppHost.csproj | 25 ++ .../DependencyInjectionAppHost/Program.cs | 9 + .../Properties/launchSettings.json | 29 +++ .../appsettings.Development.json | 8 + .../appsettings.json | 9 + .../App_Start/FilterConfig.cs | 12 + .../App_Start/RouteConfig.cs | 15 ++ .../App_Start/WebApiConfig.cs | 12 + .../Controllers/HomeController.cs | 35 +++ .../Controllers/WebApiController.cs | 33 +++ .../DependencyInjectionFramework.csproj | 40 +++ .../DependencyInjectionFramework/Global.asax | 1 + .../Global.asax.cs | 86 +++++++ .../HandlerTest.cs | 28 +++ .../Properties/launchSettings.json | 15 ++ .../DependencyInjectionFramework/Startup.cs | 20 ++ .../Views/Home/Mvc.cshtml | 4 + .../Views/Shared/_Layout.cshtml | 1 + .../Views/Web.config | 43 ++++ .../Views/_ViewStart.cshtml | 3 + .../Web.Debug.config | 30 +++ .../Web.Release.config | 31 +++ .../DependencyInjectionFramework/Web.config | 236 ++++++++++++++++++ .../DependencyInjectionFramework/favicon.ico | Bin 0 -> 32038 bytes .../Resolvers/Mvc.cs | 7 +- .../Resolvers/WebApi.cs | 5 +- .../WebObjectActivatorExtensions.cs | 10 +- .../DependencyInjectionTests.cs | 21 ++ ...NetCore.SystemWebAdapters.E2E.Tests.csproj | 1 + 30 files changed, 781 insertions(+), 9 deletions(-) create mode 100644 samples/DependencyInjection/DependencyInjectionAppHost/DependencyInjectionAppHost.csproj create mode 100644 samples/DependencyInjection/DependencyInjectionAppHost/Program.cs create mode 100644 samples/DependencyInjection/DependencyInjectionAppHost/Properties/launchSettings.json create mode 100644 samples/DependencyInjection/DependencyInjectionAppHost/appsettings.Development.json create mode 100644 samples/DependencyInjection/DependencyInjectionAppHost/appsettings.json create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/App_Start/FilterConfig.cs create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/App_Start/RouteConfig.cs create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/App_Start/WebApiConfig.cs create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Controllers/HomeController.cs create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Controllers/WebApiController.cs create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Global.asax create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Global.asax.cs create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/HandlerTest.cs create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Properties/launchSettings.json create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Startup.cs create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Views/Home/Mvc.cshtml create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Views/Shared/_Layout.cshtml create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Views/Web.config create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Views/_ViewStart.cshtml create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Web.Debug.config create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Web.Release.config create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/Web.config create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/favicon.ico create mode 100644 test/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests/DependencyInjectionTests.cs diff --git a/Microsoft.AspNetCore.SystemWebAdapters.sln b/Microsoft.AspNetCore.SystemWebAdapters.sln index 42b4df1c85..c12081e27e 100644 --- a/Microsoft.AspNetCore.SystemWebAdapters.sln +++ b/Microsoft.AspNetCore.SystemWebAdapters.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.32127.271 +# Visual Studio Version 18 +VisualStudioVersion = 18.3.11122.13 main MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SystemWebAdapters", "src\Microsoft.AspNetCore.SystemWebAdapters\Microsoft.AspNetCore.SystemWebAdapters.csproj", "{55C1BBE0-B922-46B0-8F2C-8472BC9A5F33}" EndProject @@ -128,6 +128,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.System EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp", "src\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp\Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj", "{904FF6F2-0322-9B85-7FBA-C512196D72C2}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DependencyInjection", "DependencyInjection", "{BCEFA0EE-4AC7-417E-A861-368DB3D661B1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjectionAppHost", "samples\DependencyInjection\DependencyInjectionAppHost\DependencyInjectionAppHost.csproj", "{B447F0F6-B232-D326-334B-0095BA74F988}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjectionFramework", "samples\DependencyInjection\DependencyInjectionFramework\DependencyInjectionFramework.csproj", "{4A2022AB-1ECE-E630-FD48-DD045DB4DD9F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -290,6 +296,14 @@ Global {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {904FF6F2-0322-9B85-7FBA-C512196D72C2}.Release|Any CPU.Build.0 = Release|Any CPU + {B447F0F6-B232-D326-334B-0095BA74F988}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B447F0F6-B232-D326-334B-0095BA74F988}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B447F0F6-B232-D326-334B-0095BA74F988}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B447F0F6-B232-D326-334B-0095BA74F988}.Release|Any CPU.Build.0 = Release|Any CPU + {4A2022AB-1ECE-E630-FD48-DD045DB4DD9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A2022AB-1ECE-E630-FD48-DD045DB4DD9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A2022AB-1ECE-E630-FD48-DD045DB4DD9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A2022AB-1ECE-E630-FD48-DD045DB4DD9F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -342,6 +356,9 @@ Global {6ECA1F1C-214B-BB97-27D7-319C7F1AD90F} = {012BEA62-0FF1-472E-AD5F-0D2029336701} {7A244BE9-B12E-424F-A1F1-E386C43BF0A9} = {F9DB9323-C919-49E8-8F96-B923D2F42E60} {904FF6F2-0322-9B85-7FBA-C512196D72C2} = {F9DB9323-C919-49E8-8F96-B923D2F42E60} + {BCEFA0EE-4AC7-417E-A861-368DB3D661B1} = {95915611-30BF-4AFF-AE41-5CDC6F57DCF7} + {B447F0F6-B232-D326-334B-0095BA74F988} = {BCEFA0EE-4AC7-417E-A861-368DB3D661B1} + {4A2022AB-1ECE-E630-FD48-DD045DB4DD9F} = {BCEFA0EE-4AC7-417E-A861-368DB3D661B1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DABA3C65-9D74-4EB6-9B1C-730328710EAD} diff --git a/samples/DependencyInjection/DependencyInjectionAppHost/DependencyInjectionAppHost.csproj b/samples/DependencyInjection/DependencyInjectionAppHost/DependencyInjectionAppHost.csproj new file mode 100644 index 0000000000..f244fbd97c --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionAppHost/DependencyInjectionAppHost.csproj @@ -0,0 +1,25 @@ + + + + + + Exe + net10.0-windows + enable + enable + true + + + + + + + + + + + + + + + diff --git a/samples/DependencyInjection/DependencyInjectionAppHost/Program.cs b/samples/DependencyInjection/DependencyInjectionAppHost/Program.cs new file mode 100644 index 0000000000..bff7bd2289 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionAppHost/Program.cs @@ -0,0 +1,9 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var frameworkApp = builder.AddIISExpress("iis") + .AddSiteProject("framework") + .WithDefaultIISExpressEndpoints() + .WithOtlpExporter() + .WithHttpHealthCheck("/health"); + +builder.Build().Run(); diff --git a/samples/DependencyInjection/DependencyInjectionAppHost/Properties/launchSettings.json b/samples/DependencyInjection/DependencyInjectionAppHost/Properties/launchSettings.json new file mode 100644 index 0000000000..90eb3c954c --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionAppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17095;http://localhost:15283", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL": "https://localhost:21002", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22119" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15283", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL": "http://localhost:19075", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20035" + } + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionAppHost/appsettings.Development.json b/samples/DependencyInjection/DependencyInjectionAppHost/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionAppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionAppHost/appsettings.json b/samples/DependencyInjection/DependencyInjectionAppHost/appsettings.json new file mode 100644 index 0000000000..31c092aa45 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionAppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/App_Start/FilterConfig.cs b/samples/DependencyInjection/DependencyInjectionFramework/App_Start/FilterConfig.cs new file mode 100644 index 0000000000..b40218478b --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/App_Start/FilterConfig.cs @@ -0,0 +1,12 @@ +using System.Web.Mvc; + +namespace MvcApp +{ + public class FilterConfig + { + public static void RegisterGlobalFilters(GlobalFilterCollection filters) + { + filters.Add(new HandleErrorAttribute()); + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/App_Start/RouteConfig.cs b/samples/DependencyInjection/DependencyInjectionFramework/App_Start/RouteConfig.cs new file mode 100644 index 0000000000..6213daa397 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/App_Start/RouteConfig.cs @@ -0,0 +1,15 @@ +using System.Web.Mvc; +using System.Web.Routing; + +namespace MvcApp +{ + public class RouteConfig + { + public static void RegisterRoutes(RouteCollection routes) + { + // routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + // routes.IgnoreRoute("handler"); + routes.MapMvcAttributeRoutes(); + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/App_Start/WebApiConfig.cs b/samples/DependencyInjection/DependencyInjectionFramework/App_Start/WebApiConfig.cs new file mode 100644 index 0000000000..d15f0dedc1 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/App_Start/WebApiConfig.cs @@ -0,0 +1,12 @@ +using System.Web.Http; + +namespace MvcApp +{ + public static class WebApiConfig + { + public static void Register(HttpConfiguration config) + { + config.MapHttpAttributeRoutes(); + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Controllers/HomeController.cs b/samples/DependencyInjection/DependencyInjectionFramework/Controllers/HomeController.cs new file mode 100644 index 0000000000..2dcf918038 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Controllers/HomeController.cs @@ -0,0 +1,35 @@ +using System.Web.Mvc; + +namespace MvcApp.Controllers +{ + public class HomeController : Controller + { + private readonly TransientService _transient1; + private readonly TransientService _transient2; + private readonly SingletonService _singleton; + private readonly ScopedService _scoped1; + private readonly ScopedService _scoped2; + + public HomeController( + SingletonService singleton, + ScopedService scoped1, + ScopedService scoped2, + TransientService transient1, + TransientService transient2) + { + _singleton = singleton; + _scoped1 = scoped1; + _scoped2 = scoped2; + _transient1 = transient1; + _transient2 = transient2; + } + + [Route("~/mvc")] + public ActionResult Mvc() + { + ViewBag.Message = TestService.IsValid(_singleton, _scoped1, _scoped2, _transient1, _transient2); + + return View(); + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Controllers/WebApiController.cs b/samples/DependencyInjection/DependencyInjectionFramework/Controllers/WebApiController.cs new file mode 100644 index 0000000000..afe778a6fe --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Controllers/WebApiController.cs @@ -0,0 +1,33 @@ +using System.Web.Http; + +namespace MvcApp.Controllers +{ + public class WebApiController : ApiController + { + private readonly TransientService _transient1; + private readonly TransientService _transient2; + private readonly SingletonService _singleton; + private readonly ScopedService _scoped1; + private readonly ScopedService _scoped2; + + public WebApiController( + SingletonService singleton, + ScopedService scoped1, + ScopedService scoped2, + TransientService transient1, + TransientService transient2) + { + _singleton = singleton; + _scoped1 = scoped1; + _scoped2 = scoped2; + _transient1 = transient1; + _transient2 = transient2; + } + + [Route("api/")] + public IHttpActionResult Get() + { + return Ok(TestService.IsValid(_singleton, _scoped1, _scoped2, _transient1, _transient2)); + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj b/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj new file mode 100644 index 0000000000..f783d91f4c --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj @@ -0,0 +1,40 @@ + + + net481 + + + 7.3 + disable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Global.asax b/samples/DependencyInjection/DependencyInjectionFramework/Global.asax new file mode 100644 index 0000000000..a0c797168f --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="MvcApp.MvcApplication" Language="C#" %> diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Global.asax.cs b/samples/DependencyInjection/DependencyInjectionFramework/Global.asax.cs new file mode 100644 index 0000000000..81561ff683 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Global.asax.cs @@ -0,0 +1,86 @@ +using System.Threading; +using System.Web; +using System.Web.Http; +using System.Web.Mvc; +using System.Web.Optimization; +using System.Web.Routing; +using Microsoft.AspNetCore.SystemWebAdapters.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace MvcApp +{ + public class MvcApplication : HttpApplication + { + protected void Application_Start() + { + HttpApplicationHost.RegisterHost(builder => + { + builder.AddServiceDefaults(); + builder.AddSystemWebDependencyInjection(); + + builder.Services.AddSingleton(SingletonService.Instance); + builder.Services.AddScoped(); + builder.Services.AddTransient(); + }); + + AreaRegistration.RegisterAllAreas(); + GlobalConfiguration.Configure(WebApiConfig.Register); + FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); + RouteConfig.RegisterRoutes(RouteTable.Routes); + } + } + + public class SingletonService + { + public static SingletonService Instance { get; } = new SingletonService(); + } + + public class TransientService + { + } + + public class ScopedService + { + } + + public class TestService + { + public static bool IsValid(SingletonService singleton, TransientService transient1, TransientService transient2) + { + if (!ReferenceEquals(SingletonService.Instance, singleton)) + { + return false; + } + + if (ReferenceEquals(transient1, transient2)) + { + return false; + } + + return true; + } + + public static bool IsValid(SingletonService singleton, ScopedService scoped1, ScopedService scoped2, TransientService transient1, TransientService transient2) + { + IsValid(singleton, transient1, transient2); + + if (!ReferenceEquals(scoped1, scoped2)) + { + return false; + } + + using (var testScope = HttpRuntime.WebObjectActivator.CreateScope()) + { + var scopedInNewScope = testScope.ServiceProvider.GetRequiredService(); + if (ReferenceEquals(scoped1, scopedInNewScope)) + { + return false; + } + } + + return true; + } + } +} + diff --git a/samples/DependencyInjection/DependencyInjectionFramework/HandlerTest.cs b/samples/DependencyInjection/DependencyInjectionFramework/HandlerTest.cs new file mode 100644 index 0000000000..cf3be3dbd3 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/HandlerTest.cs @@ -0,0 +1,28 @@ +using System.Text.Json; +using System.Web; +using MvcApp; + +namespace DependencyInjectionFramework +{ + public class HandlerTest : IHttpHandler + { + private readonly TransientService _transient1; + private readonly TransientService _transient2; + private readonly SingletonService _singleton; + + // NOTE: Handlers do not support scoped services + public HandlerTest(SingletonService singleton, TransientService transient1, TransientService transient2) + { + _singleton = singleton; + _transient1 = transient1; + _transient2 = transient2; + } + + public bool IsReusable => false; + + public void ProcessRequest(HttpContext context) + { + context.Response.Write(TestService.IsValid(_singleton, _transient1, _transient2)); + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Properties/launchSettings.json b/samples/DependencyInjection/DependencyInjectionFramework/Properties/launchSettings.json new file mode 100644 index 0000000000..869f8598ef --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "https://localhost:44339" + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Startup.cs b/samples/DependencyInjection/DependencyInjectionFramework/Startup.cs new file mode 100644 index 0000000000..b3852319ef --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Startup.cs @@ -0,0 +1,20 @@ +using Microsoft.Owin; +using Owin; + +[assembly: OwinStartup(typeof(MvcApp.Startup))] +namespace MvcApp +{ + public partial class Startup + { + public void Configuration(IAppBuilder app) + { + app.Map("/health", app2 => + { + app2.Run(ctx => + { + return ctx.Response.WriteAsync("OK"); + }); + }); + } + } +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Views/Home/Mvc.cshtml b/samples/DependencyInjection/DependencyInjectionFramework/Views/Home/Mvc.cshtml new file mode 100644 index 0000000000..f1fb2a02c7 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Views/Home/Mvc.cshtml @@ -0,0 +1,4 @@ +@{ + ViewBag.Title = "Home Page"; +} +@ViewBag.Message diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Views/Shared/_Layout.cshtml b/samples/DependencyInjection/DependencyInjectionFramework/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..7d46087411 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Views/Shared/_Layout.cshtml @@ -0,0 +1 @@ +@RenderBody() diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Views/Web.config b/samples/DependencyInjection/DependencyInjectionFramework/Views/Web.config new file mode 100644 index 0000000000..b3b0a8c79c --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Views/Web.config @@ -0,0 +1,43 @@ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Views/_ViewStart.cshtml b/samples/DependencyInjection/DependencyInjectionFramework/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..2de62418c0 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +} diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Web.Debug.config b/samples/DependencyInjection/DependencyInjectionFramework/Web.Debug.config new file mode 100644 index 0000000000..d7712aaf17 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Web.Release.config b/samples/DependencyInjection/DependencyInjectionFramework/Web.Release.config new file mode 100644 index 0000000000..28a4d5fcc3 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/samples/DependencyInjection/DependencyInjectionFramework/Web.config b/samples/DependencyInjection/DependencyInjectionFramework/Web.config new file mode 100644 index 0000000000..c44dd0f396 --- /dev/null +++ b/samples/DependencyInjection/DependencyInjectionFramework/Web.config @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/DependencyInjection/DependencyInjectionFramework/favicon.ico b/samples/DependencyInjection/DependencyInjectionFramework/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a3a799985c43bc7309d701b2cad129023377dc71 GIT binary patch literal 32038 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 public static void AddMvcDependencyInjection(this HttpApplicationHostBuilder builder) { - builder.Services.TryAddSingleton(); - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(sp => sp.GetRequiredService())); - builder.Services.TryAddSingleton(sp => sp.GetRequiredService()); - builder.Services.TryAddSingleton(sp => sp.GetRequiredService()); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); } private sealed class MvcAdapterDependencyResolver : IDependencyRegistrar, IDependencyResolver, IViewPageActivator, IControllerActivator, IDisposable diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs index 401259caf9..12d33c6cdd 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs @@ -5,8 +5,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.AspNetCore.SystemWebAdapters.Hosting; +using System.Net.Http; using System.Web.Http; +using System.Web.Http.Controllers; using System.Web.Http.Dependencies; +using System.Web.Http.Dispatcher; namespace System.Web { @@ -66,7 +69,7 @@ public Scope(IServiceScope scope) void IDisposable.Dispose() => _scope.Dispose(); - object IDependencyScope.GetService(Type serviceType) => _scope.ServiceProvider.GetService(serviceType); + object IDependencyScope.GetService(Type serviceType) => ActivatorUtilities.GetServiceOrCreateInstance(_scope.ServiceProvider, serviceType); IEnumerable IDependencyScope.GetServices(Type serviceType) => _scope.ServiceProvider.GetServices(serviceType); } diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/WebObjectActivatorExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/WebObjectActivatorExtensions.cs index 36fe19b81d..c159435a85 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/WebObjectActivatorExtensions.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices/WebObjectActivatorExtensions.cs @@ -83,8 +83,14 @@ public bool Enable(bool force) { return null; } - - return CreateNonPublicInstance(serviceType); + else if (serviceType.IsPublic && serviceType.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Length > 0) + { + return ActivatorUtilities.CreateInstance(_services, serviceType); + } + else + { + return CreateNonPublicInstance(serviceType); + } // The implementation of dependency injection in System.Web expects to be able to create instances // of non-public and unregistered types. diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests/DependencyInjectionTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests/DependencyInjectionTests.cs new file mode 100644 index 0000000000..aa84e4529e --- /dev/null +++ b/test/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests/DependencyInjectionTests.cs @@ -0,0 +1,21 @@ +using Projects; +using Xunit; + +namespace Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests; + +public class DependencyInjectionTests(AspireFixture aspire) : IClassFixture> +{ + [InlineData("/handler")] + [InlineData("/mvc")] + [InlineData("/api")] + [Theory] + public async Task CheckFrameworkDI(string path) + { + var app = await aspire.GetApplicationAsync(); + using var client = app.CreateHttpClient("framework"); + var response = await client.GetStringAsync(new Uri(path, UriKind.Relative)); + + Assert.True(bool.Parse(response)); + } +} + diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests.csproj b/test/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests.csproj index 0ef846edb8..a9e40ccd58 100644 --- a/test/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests.csproj +++ b/test/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests/Microsoft.AspNetCore.SystemWebAdapters.E2E.Tests.csproj @@ -19,6 +19,7 @@ + From ff49004e9b4fb4080936222dd8a7cc3aed8d508b Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Thu, 23 Oct 2025 17:53:35 -0700 Subject: [PATCH 12/15] some clean up from tests --- .../DependencyInjectionFramework.csproj | 12 ------------ .../FrameworkDependencyInjectionGenerator.cs | 2 +- ...NetCore.SystemWebAdapters.Analyzers.CSharp.csproj | 5 +++-- .../{Resolvers => Templates}/Mvc.cs | 0 .../{Resolvers => Templates}/WebApi.cs | 0 5 files changed, 4 insertions(+), 15 deletions(-) rename src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/{Resolvers => Templates}/Mvc.cs (100%) rename src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/{Resolvers => Templates}/WebApi.cs (100%) diff --git a/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj b/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj index f783d91f4c..f23a4cba2f 100644 --- a/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj +++ b/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj @@ -25,16 +25,4 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs index 90c4862b37..8e5e15350b 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/FrameworkDependencyInjectionGenerator.cs @@ -51,7 +51,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) foreach (var registrarSource in registrars) { using var stream = typeof(FrameworkDependencyInjectionGenerator).Assembly - .GetManifestResourceStream($"Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.Resolvers.{registrarSource.Name}.cs"); + .GetManifestResourceStream($"Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.Templates.{registrarSource.Name}.cs"); if (stream is null) { diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj index 710c6bb674..4d57479f84 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj +++ b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp.csproj @@ -13,7 +13,8 @@ - - + + + diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Templates/Mvc.cs similarity index 100% rename from src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/Mvc.cs rename to src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Templates/Mvc.cs diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Templates/WebApi.cs similarity index 100% rename from src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Resolvers/WebApi.cs rename to src/Microsoft.AspNetCore.SystemWebAdapters.Analyzers.CSharp/Templates/WebApi.cs From d5ac0efa4d36aa0934bc91025405040e8d92487c Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Mon, 27 Oct 2025 09:51:38 -0700 Subject: [PATCH 13/15] add back csharp --- .../DependencyInjectionFramework.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj b/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj index f23a4cba2f..edb2d39ef9 100644 --- a/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj +++ b/samples/DependencyInjection/DependencyInjectionFramework/DependencyInjectionFramework.csproj @@ -25,4 +25,7 @@ + + + \ No newline at end of file From 7716e1d15e554458aa8c2b991e2aa1ae33629cad Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Mon, 27 Oct 2025 10:41:05 -0700 Subject: [PATCH 14/15] add webforms page --- samples/AppConfig/AppConfigCore/Program.cs | 2 -- .../AppConfig/AppConfigFramework/Web.config | 10 ++++++-- .../AuthRemoteFormsAuthFramework/Web.config | 8 ++++++- .../AuthRemoteIdentityFramework/Web.config | 8 ++++++- .../DependencyInjectionFramework/Web.config | 8 ++++++- .../webforms.aspx | 2 ++ .../webforms.aspx.cs | 23 +++++++++++++++++++ samples/Directory.Build.props | 4 ---- samples/Directory.Packages.props | 2 ++ .../MachineKey/MachineKeyFramework/Web.config | 8 ++++++- samples/Modules/ModulesFramework/Web.config | 8 ++++++- .../SessionRemoteFramework/Web.config | 8 ++++++- .../WebFormsToBlazorFramework/Web.config | 8 ++++++- .../DependencyInjectionTests.cs | 1 + 14 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/webforms.aspx create mode 100644 samples/DependencyInjection/DependencyInjectionFramework/webforms.aspx.cs diff --git a/samples/AppConfig/AppConfigCore/Program.cs b/samples/AppConfig/AppConfigCore/Program.cs index de7ba92124..a1d8b5da37 100644 --- a/samples/AppConfig/AppConfigCore/Program.cs +++ b/samples/AppConfig/AppConfigCore/Program.cs @@ -1,6 +1,4 @@ -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.SystemWebAdapters; -using Microsoft.AspNetCore.SystemWebAdapters.Features; var builder = WebApplication.CreateBuilder(args); diff --git a/samples/AppConfig/AppConfigFramework/Web.config b/samples/AppConfig/AppConfigFramework/Web.config index 88d5f4a0de..be83c22e80 100644 --- a/samples/AppConfig/AppConfigFramework/Web.config +++ b/samples/AppConfig/AppConfigFramework/Web.config @@ -1,4 +1,4 @@ - + @@ -6,7 +6,7 @@ - + @@ -154,4 +154,10 @@ + + + + + + diff --git a/samples/AuthRemoteFormsAuth/AuthRemoteFormsAuthFramework/Web.config b/samples/AuthRemoteFormsAuth/AuthRemoteFormsAuthFramework/Web.config index 240b79041a..489de7374d 100644 --- a/samples/AuthRemoteFormsAuth/AuthRemoteFormsAuthFramework/Web.config +++ b/samples/AuthRemoteFormsAuth/AuthRemoteFormsAuthFramework/Web.config @@ -1,4 +1,4 @@ - +
@@ -189,4 +189,10 @@ + + + + + + \ No newline at end of file diff --git a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Web.config b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Web.config index 7fe144ee87..33f72c92e2 100644 --- a/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Web.config +++ b/samples/AuthRemoteIdentity/AuthRemoteIdentityFramework/Web.config @@ -1,4 +1,4 @@ - + - - true - Overwrite diff --git a/samples/Directory.Packages.props b/samples/Directory.Packages.props index 6654ed697f..10303e3c41 100644 --- a/samples/Directory.Packages.props +++ b/samples/Directory.Packages.props @@ -39,6 +39,8 @@ + + diff --git a/samples/MachineKey/MachineKeyFramework/Web.config b/samples/MachineKey/MachineKeyFramework/Web.config index a0c770ec94..3775525757 100644 --- a/samples/MachineKey/MachineKeyFramework/Web.config +++ b/samples/MachineKey/MachineKeyFramework/Web.config @@ -1,4 +1,4 @@ - + @@ -152,4 +152,10 @@ + + + + + + \ No newline at end of file diff --git a/samples/Modules/ModulesFramework/Web.config b/samples/Modules/ModulesFramework/Web.config index 01e6bb3127..d42b7e598b 100644 --- a/samples/Modules/ModulesFramework/Web.config +++ b/samples/Modules/ModulesFramework/Web.config @@ -1,4 +1,4 @@ - +