From 069de2c7f2ab71dd2a4716ab2305559392a563c0 Mon Sep 17 00:00:00 2001 From: Travis Illig Date: Tue, 1 Sep 2026 08:02:51 -0700 Subject: [PATCH 1/6] Enable nullable, analyzers, and code style enforcement LangVersion=latest is what makes the rest possible: net481 and netstandard2.0 default to C# 7.3, and Nullable=enable is a hard build error (CS8630) below C# 8. Raising it also lets IDE0161 reach the .NET Framework examples, so file-scoped namespaces finally apply everywhere. AnalysisLevel is latest-recommended rather than latest-all. The five suppressed rules are documented inline; the theme is that ASP.NET dictates certain names, and logging-performance boilerplate belongs in production code rather than in a ten-line teaching sample. NU1900 is excluded from TreatWarningsAsErrors. It reports that the vulnerability audit feed was unreachable, which shouldn't fail a build. Part of #32 --- Directory.Build.props | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Directory.Build.props diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..39f92d6 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,27 @@ + + + + latest + enable + latest-recommended + true + + + + true + + NU1900 + + + + + $(NoWarn);CA1707;CA1716;CA1848;CA1873;CA5368 + + From f089e7856c50b437f6583ba069a3ed553d1f7d61 Mon Sep 17 00:00:00 2001 From: Travis Illig Date: Tue, 1 Sep 2026 08:03:57 -0700 Subject: [PATCH 2/6] Convert to file-scoped namespaces and drop redundant usings Mechanical follow-on from raising LangVersion. dotnet format converted the 57 remaining block-scoped namespace files; the six it skipped are tool-generated designer and WCF proxy files, which Roslyn excludes from style rules by design. Also removes 25 usings that ImplicitUsings already provides. Part of #32 --- .../Services/IValuesService.cs | 4 +- .../Services/ValuesService.cs | 5 +- .../AutofacModule.cs | 1 - .../Services/ValuesService.cs | 4 +- .../AppenderMetadata.cs | 2 +- .../AppenderNameAttribute.cs | 3 +- .../AttributeMetadataAppender.cs | 4 +- .../InterfaceManualMetadataAdapter.cs | 4 +- src/AttributeMetadataExample/Log.cs | 4 +- src/AttributeMetadataExample/Program.cs | 3 +- .../StringManualMetadataAppender.cs | 4 +- .../TypedManualMetadataAppender.cs | 4 +- src/ConfigurationExample/Program.cs | 5 +- src/ConfigurationExampleInterface/IPlugin.cs | 27 ++- .../ExternalPlugin.cs | 29 ++- .../HostedService.cs | 4 +- src/GenericHostBuilderExample/ILogger.cs | 4 +- src/GenericHostBuilderExample/Logger.cs | 5 +- src/GenericHostBuilderExample/Program.cs | 3 +- .../BaseDependency.cs | 4 +- .../IDependency.cs | 4 +- .../ManualTenantIdentificationStrategy.cs | 4 +- .../Program.cs | 7 +- .../Controllers/HomeController.cs | 107 +++++---- .../Controllers/Tenant1Controller.cs | 41 ++-- .../Controllers/Tenant2Controller.cs | 41 ++-- .../Dependencies/BaseDependency.cs | 43 ++-- .../Dependencies/DefaultTenantDependency.cs | 13 +- .../Dependencies/IDependency.cs | 29 ++- .../Dependencies/Tenant1Dependency.cs | 13 +- .../Dependencies/Tenant2Dependency.cs | 13 +- .../Global.asax.cs | 221 +++++++++--------- .../Models/IndexModel.cs | 135 ++++++----- .../RequestParameterStrategy.cs | 33 ++- .../Dependencies/BaseDependency.cs | 43 ++-- .../Dependencies/DefaultTenantDependency.cs | 13 +- .../Dependencies/IDependency.cs | 29 ++- .../Dependencies/Tenant1Dependency.cs | 13 +- .../Dependencies/Tenant2Dependency.cs | 13 +- .../GetServiceInfoResponse.cs | 95 ++++---- .../Global.asax.cs | 153 ++++++------ .../IMetadataConsumer.cs | 15 +- .../IMultitenantService.cs | 13 +- .../MetadataConsumerBuddyClass.cs | 15 +- .../BaseImplementation.cs | 37 ++- .../ServiceInfoBuilder.cs | 81 ++++--- .../Tenant1Implementation.cs | 41 ++-- .../Tenant2Implementation.cs | 41 ++-- src/MvcExample/Controllers/HomeController.cs | 53 +++-- src/MvcExample/CustomActionFilterAttribute.cs | 23 +- src/MvcExample/CustomViewPage.cs | 19 +- .../Dependencies/FilterDependency.cs | 29 ++- .../Dependencies/IFilterDependency.cs | 25 +- .../Dependencies/IViewDependency.cs | 27 ++- src/MvcExample/Dependencies/ViewDependency.cs | 43 ++-- src/MvcExample/Global.asax.cs | 127 +++++----- src/MvcExample/Models/DependencyValueModel.cs | 45 ++-- src/WcfExample/Dependencies/Dependency.cs | 41 ++-- src/WcfExample/Dependencies/IDependency.cs | 29 ++- src/WcfExample/GetServiceInfoResponse.cs | 51 ++-- src/WcfExample/Global.asax.cs | 33 ++- src/WcfExample/HostFactoryService.svc.cs | 33 ++- src/WcfExample/IService.cs | 15 +- src/WcfExample/WebHostFactoryService.svc.cs | 45 ++-- .../CustomActionFilter.cs | 35 ++- .../FirstMiddleware.cs | 25 +- src/WebApiExample.OwinSelfHost/ILogger.cs | 9 +- src/WebApiExample.OwinSelfHost/Logger.cs | 11 +- src/WebApiExample.OwinSelfHost/Program.cs | 45 ++-- .../SecondMiddleware.cs | 25 +- src/WebApiExample.OwinSelfHost/Startup.cs | 99 ++++---- .../TestController.cs | 25 +- src/WebFormsExample/About.aspx.cs | 7 +- src/WebFormsExample/Default.aspx.cs | 25 +- .../Dependencies/Dependency.cs | 41 ++-- .../Dependencies/IDependency.cs | 29 ++- src/WebFormsExample/Global.asax.cs | 59 +++-- src/WebFormsExample/Site.Master.cs | 9 +- 78 files changed, 1209 insertions(+), 1302 deletions(-) diff --git a/src/AspNetCoreExample/Services/IValuesService.cs b/src/AspNetCoreExample/Services/IValuesService.cs index 856028a..d33376e 100644 --- a/src/AspNetCoreExample/Services/IValuesService.cs +++ b/src/AspNetCoreExample/Services/IValuesService.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace AspNetCoreExample.Services; +namespace AspNetCoreExample.Services; public interface IValuesService { diff --git a/src/AspNetCoreExample/Services/ValuesService.cs b/src/AspNetCoreExample/Services/ValuesService.cs index 26180cb..0436760 100644 --- a/src/AspNetCoreExample/Services/ValuesService.cs +++ b/src/AspNetCoreExample/Services/ValuesService.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using Microsoft.Extensions.Logging; - -namespace AspNetCoreExample.Services; +namespace AspNetCoreExample.Services; public class ValuesService : IValuesService { diff --git a/src/AspNetCoreNoStartupExample/AutofacModule.cs b/src/AspNetCoreNoStartupExample/AutofacModule.cs index 4f48707..3f6cd58 100644 --- a/src/AspNetCoreNoStartupExample/AutofacModule.cs +++ b/src/AspNetCoreNoStartupExample/AutofacModule.cs @@ -1,6 +1,5 @@ using AspNetCoreNoStartupExample.Services; using Autofac; -using Microsoft.Extensions.Logging; namespace AspNetCoreNoStartupExample; diff --git a/src/AspNetCoreNoStartupExample/Services/ValuesService.cs b/src/AspNetCoreNoStartupExample/Services/ValuesService.cs index 9ea4e62..6bfae60 100644 --- a/src/AspNetCoreNoStartupExample/Services/ValuesService.cs +++ b/src/AspNetCoreNoStartupExample/Services/ValuesService.cs @@ -1,6 +1,4 @@ -using Microsoft.Extensions.Logging; - -namespace AspNetCoreNoStartupExample.Services; +namespace AspNetCoreNoStartupExample.Services; public class ValuesService : IValuesService { diff --git a/src/AttributeMetadataExample/AppenderMetadata.cs b/src/AttributeMetadataExample/AppenderMetadata.cs index 879e0e8..6803637 100644 --- a/src/AttributeMetadataExample/AppenderMetadata.cs +++ b/src/AttributeMetadataExample/AppenderMetadata.cs @@ -2,7 +2,7 @@ public class AppenderMetadata { - public string AppenderName + public string? AppenderName { get; set; } diff --git a/src/AttributeMetadataExample/AppenderNameAttribute.cs b/src/AttributeMetadataExample/AppenderNameAttribute.cs index 84b255e..bb8e648 100644 --- a/src/AttributeMetadataExample/AppenderNameAttribute.cs +++ b/src/AttributeMetadataExample/AppenderNameAttribute.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel.Composition; +using System.ComponentModel.Composition; namespace AttributeMetadataExample; diff --git a/src/AttributeMetadataExample/AttributeMetadataAppender.cs b/src/AttributeMetadataExample/AttributeMetadataAppender.cs index d7153aa..191f5ab 100644 --- a/src/AttributeMetadataExample/AttributeMetadataAppender.cs +++ b/src/AttributeMetadataExample/AttributeMetadataAppender.cs @@ -1,6 +1,4 @@ -using System; - -namespace AttributeMetadataExample; +namespace AttributeMetadataExample; [AppenderName("attributed")] public class AttributeMetadataAppender : ILogAppender diff --git a/src/AttributeMetadataExample/InterfaceManualMetadataAdapter.cs b/src/AttributeMetadataExample/InterfaceManualMetadataAdapter.cs index 7b3069e..48ff950 100644 --- a/src/AttributeMetadataExample/InterfaceManualMetadataAdapter.cs +++ b/src/AttributeMetadataExample/InterfaceManualMetadataAdapter.cs @@ -1,6 +1,4 @@ -using System; - -namespace AttributeMetadataExample; +namespace AttributeMetadataExample; public class InterfaceManualMetadataAdapter : ILogAppender { diff --git a/src/AttributeMetadataExample/Log.cs b/src/AttributeMetadataExample/Log.cs index 97feb25..85e5418 100644 --- a/src/AttributeMetadataExample/Log.cs +++ b/src/AttributeMetadataExample/Log.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Linq; -using Autofac.Features.Metadata; +using Autofac.Features.Metadata; namespace AttributeMetadataExample; diff --git a/src/AttributeMetadataExample/Program.cs b/src/AttributeMetadataExample/Program.cs index 5e03821..653eb10 100644 --- a/src/AttributeMetadataExample/Program.cs +++ b/src/AttributeMetadataExample/Program.cs @@ -1,5 +1,4 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using Autofac; using Autofac.Extras.AttributeMetadata; using Autofac.Features.AttributeFilters; diff --git a/src/AttributeMetadataExample/StringManualMetadataAppender.cs b/src/AttributeMetadataExample/StringManualMetadataAppender.cs index f94015c..a4ae935 100644 --- a/src/AttributeMetadataExample/StringManualMetadataAppender.cs +++ b/src/AttributeMetadataExample/StringManualMetadataAppender.cs @@ -1,6 +1,4 @@ -using System; - -namespace AttributeMetadataExample; +namespace AttributeMetadataExample; public class StringManualMetadataAppender : ILogAppender { diff --git a/src/AttributeMetadataExample/TypedManualMetadataAppender.cs b/src/AttributeMetadataExample/TypedManualMetadataAppender.cs index 9396c01..ebbee62 100644 --- a/src/AttributeMetadataExample/TypedManualMetadataAppender.cs +++ b/src/AttributeMetadataExample/TypedManualMetadataAppender.cs @@ -1,6 +1,4 @@ -using System; - -namespace AttributeMetadataExample; +namespace AttributeMetadataExample; public class TypedManualMetadataAppender : ILogAppender { diff --git a/src/ConfigurationExample/Program.cs b/src/ConfigurationExample/Program.cs index a032989..f9a2f54 100644 --- a/src/ConfigurationExample/Program.cs +++ b/src/ConfigurationExample/Program.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; +using System.Diagnostics; using System.Reflection; using System.Runtime.Loader; using Autofac; diff --git a/src/ConfigurationExampleInterface/IPlugin.cs b/src/ConfigurationExampleInterface/IPlugin.cs index 3bb6f82..63bf83b 100644 --- a/src/ConfigurationExampleInterface/IPlugin.cs +++ b/src/ConfigurationExampleInterface/IPlugin.cs @@ -1,20 +1,19 @@ -namespace ConfigurationExampleInterface +namespace ConfigurationExampleInterface; + +/// +/// Simple plugin interface used to illustrate assembly loading/plugin handling +/// via configuration. See the "ConfigurationExample" project for usage. +/// +public interface IPlugin { /// - /// Simple plugin interface used to illustrate assembly loading/plugin handling - /// via configuration. See the "ConfigurationExample" project for usage. + /// Gets the name of the plugin. /// - public interface IPlugin + /// + /// A with the plugin name. Likely just the type name for illustrative purposes. + /// + string Name { - /// - /// Gets the name of the plugin. - /// - /// - /// A with the plugin name. Likely just the type name for illustrative purposes. - /// - string Name - { - get; - } + get; } } diff --git a/src/ConfigurationExamplePlugin/ExternalPlugin.cs b/src/ConfigurationExamplePlugin/ExternalPlugin.cs index f77aea6..47c54ca 100644 --- a/src/ConfigurationExamplePlugin/ExternalPlugin.cs +++ b/src/ConfigurationExamplePlugin/ExternalPlugin.cs @@ -1,25 +1,24 @@ using ConfigurationExampleInterface; -namespace ConfigurationExamplePlugin +namespace ConfigurationExamplePlugin; + +/// +/// Implementation of the plugin interface that will be loaded from an external assembly. See the "ConfigurationExample" +/// project for more details. +/// +public class ExternalPlugin : IPlugin { /// - /// Implementation of the plugin interface that will be loaded from an external assembly. See the "ConfigurationExample" - /// project for more details. + /// Gets the name of the plugin. /// - public class ExternalPlugin : IPlugin + /// + /// Always returns ExternalPlugin. + /// + public string Name { - /// - /// Gets the name of the plugin. - /// - /// - /// Always returns ExternalPlugin. - /// - public string Name + get { - get - { - return "ExternalPlugin"; - } + return "ExternalPlugin"; } } } diff --git a/src/GenericHostBuilderExample/HostedService.cs b/src/GenericHostBuilderExample/HostedService.cs index 764fa59..27d31e8 100644 --- a/src/GenericHostBuilderExample/HostedService.cs +++ b/src/GenericHostBuilderExample/HostedService.cs @@ -1,6 +1,4 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Hosting; namespace GenericHostBuilderExample; diff --git a/src/GenericHostBuilderExample/ILogger.cs b/src/GenericHostBuilderExample/ILogger.cs index 163e00f..db7d410 100644 --- a/src/GenericHostBuilderExample/ILogger.cs +++ b/src/GenericHostBuilderExample/ILogger.cs @@ -1,6 +1,4 @@ -using System.Threading.Tasks; - -namespace GenericHostBuilderExample; +namespace GenericHostBuilderExample; internal interface ILogger { diff --git a/src/GenericHostBuilderExample/Logger.cs b/src/GenericHostBuilderExample/Logger.cs index 386fe3d..95dc317 100644 --- a/src/GenericHostBuilderExample/Logger.cs +++ b/src/GenericHostBuilderExample/Logger.cs @@ -1,7 +1,4 @@ -using System; -using System.Threading.Tasks; - -namespace GenericHostBuilderExample; +namespace GenericHostBuilderExample; internal class Logger : ILogger { diff --git a/src/GenericHostBuilderExample/Program.cs b/src/GenericHostBuilderExample/Program.cs index ab016e5..96a3c1d 100644 --- a/src/GenericHostBuilderExample/Program.cs +++ b/src/GenericHostBuilderExample/Program.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using Autofac; +using Autofac; using Autofac.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/src/MultitenantExample.ConsoleApplication/BaseDependency.cs b/src/MultitenantExample.ConsoleApplication/BaseDependency.cs index 80ff211..40208ab 100644 --- a/src/MultitenantExample.ConsoleApplication/BaseDependency.cs +++ b/src/MultitenantExample.ConsoleApplication/BaseDependency.cs @@ -1,6 +1,4 @@ -using System; - -namespace MultitenantExample.ConsoleApplication; +namespace MultitenantExample.ConsoleApplication; /// /// Base class for dependencies. Used simply to avoid redundant code; it's not diff --git a/src/MultitenantExample.ConsoleApplication/IDependency.cs b/src/MultitenantExample.ConsoleApplication/IDependency.cs index 3ee933b..3337134 100644 --- a/src/MultitenantExample.ConsoleApplication/IDependency.cs +++ b/src/MultitenantExample.ConsoleApplication/IDependency.cs @@ -1,6 +1,4 @@ -using System; - -namespace MultitenantExample.ConsoleApplication; +namespace MultitenantExample.ConsoleApplication; /// /// Demonstration dependency interface that allows you to inspect the unique diff --git a/src/MultitenantExample.ConsoleApplication/ManualTenantIdentificationStrategy.cs b/src/MultitenantExample.ConsoleApplication/ManualTenantIdentificationStrategy.cs index 0aadfeb..a17b669 100644 --- a/src/MultitenantExample.ConsoleApplication/ManualTenantIdentificationStrategy.cs +++ b/src/MultitenantExample.ConsoleApplication/ManualTenantIdentificationStrategy.cs @@ -25,7 +25,7 @@ public class ManualTenantIdentificationStrategy : ITenantIdentificationStrategy /// /// when the current tenant ID is requested. /// - public object CurrentTenantId + public object? CurrentTenantId { get; set; } @@ -38,7 +38,7 @@ public object CurrentTenantId /// /// This implementation always returns . /// - public bool TryIdentifyTenant(out object tenantId) + public bool TryIdentifyTenant(out object? tenantId) { if (CurrentTenantId.ToString() == "0") { diff --git a/src/MultitenantExample.ConsoleApplication/Program.cs b/src/MultitenantExample.ConsoleApplication/Program.cs index f7c2059..3574ba8 100644 --- a/src/MultitenantExample.ConsoleApplication/Program.cs +++ b/src/MultitenantExample.ConsoleApplication/Program.cs @@ -1,5 +1,4 @@ -using System; -using Autofac; +using Autofac; using Autofac.Multitenant; namespace MultitenantExample.ConsoleApplication; @@ -20,12 +19,12 @@ public class Program /// /// The container from which dependencies will be resolved. /// - private static IContainer _container; + private static IContainer? _container; /// /// Strategy used for identifying the current tenant with multitenant DI. /// - private static ManualTenantIdentificationStrategy _tenantIdentifier; + private static ManualTenantIdentificationStrategy? _tenantIdentifier; /// /// Demo program entry point. diff --git a/src/MultitenantExample.MvcApplication/Controllers/HomeController.cs b/src/MultitenantExample.MvcApplication/Controllers/HomeController.cs index 85ff789..b476133 100644 --- a/src/MultitenantExample.MvcApplication/Controllers/HomeController.cs +++ b/src/MultitenantExample.MvcApplication/Controllers/HomeController.cs @@ -5,72 +5,71 @@ using MultitenantExample.MvcApplication.WcfMetadataConsumer; using MultitenantExample.MvcApplication.WcfService; -namespace MultitenantExample.MvcApplication.Controllers +namespace MultitenantExample.MvcApplication.Controllers; + +[HandleError] +public class HomeController : Controller { - [HandleError] - public class HomeController : Controller + public HomeController(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy, IMultitenantService standardService, IMetadataConsumer metadataService) { - public HomeController(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy, IMultitenantService standardService, IMetadataConsumer metadataService) - { - Dependency = dependency; - TenantIdentificationStrategy = tenantIdStrategy; - StandardServiceProxy = standardService; - MetadataServiceProxy = metadataService; - } + Dependency = dependency; + TenantIdentificationStrategy = tenantIdStrategy; + StandardServiceProxy = standardService; + MetadataServiceProxy = metadataService; + } - public IDependency Dependency - { - get; set; - } + public IDependency Dependency + { + get; set; + } - public IMetadataConsumer MetadataServiceProxy - { - get; set; - } + public IMetadataConsumer MetadataServiceProxy + { + get; set; + } - public IMultitenantService StandardServiceProxy - { - get; set; - } + public IMultitenantService StandardServiceProxy + { + get; set; + } - public ITenantIdentificationStrategy TenantIdentificationStrategy - { - get; set; - } + public ITenantIdentificationStrategy TenantIdentificationStrategy + { + get; set; + } - public ActionResult About() - { - return View(); - } + public ActionResult About() + { + return View(); + } - public virtual ActionResult Index() - { - var model = BuildIndexModel(); - return View(model); - } + public virtual ActionResult Index() + { + var model = BuildIndexModel(); + return View(model); + } - protected virtual IndexModel BuildIndexModel() + protected virtual IndexModel BuildIndexModel() + { + var model = new IndexModel() { - var model = new IndexModel() - { - ControllerTypeName = GetType().Name, - DependencyInstanceId = Dependency.InstanceId, - DependencyTypeName = Dependency.GetType().Name, - TenantId = GetTenantId(), - StandardServiceInfo = StandardServiceProxy.GetServiceInfo(new MultitenantExample.MvcApplication.WcfService.GetServiceInfoRequest()), - MetadataServiceInfo = MetadataServiceProxy.GetServiceInfo(new MultitenantExample.MvcApplication.WcfMetadataConsumer.GetServiceInfoRequest()) - }; - return model; - } + ControllerTypeName = GetType().Name, + DependencyInstanceId = Dependency.InstanceId, + DependencyTypeName = Dependency.GetType().Name, + TenantId = GetTenantId(), + StandardServiceInfo = StandardServiceProxy.GetServiceInfo(new MultitenantExample.MvcApplication.WcfService.GetServiceInfoRequest()), + MetadataServiceInfo = MetadataServiceProxy.GetServiceInfo(new MultitenantExample.MvcApplication.WcfMetadataConsumer.GetServiceInfoRequest()) + }; + return model; + } - private object GetTenantId() + private object GetTenantId() + { + var success = TenantIdentificationStrategy.TryIdentifyTenant(out var tenantId); + if (!success || tenantId == null) { - var success = TenantIdentificationStrategy.TryIdentifyTenant(out var tenantId); - if (!success || tenantId == null) - { - return "[Default Tenant]"; - } - return tenantId; + return "[Default Tenant]"; } + return tenantId; } } diff --git a/src/MultitenantExample.MvcApplication/Controllers/Tenant1Controller.cs b/src/MultitenantExample.MvcApplication/Controllers/Tenant1Controller.cs index 6571c53..bc25954 100644 --- a/src/MultitenantExample.MvcApplication/Controllers/Tenant1Controller.cs +++ b/src/MultitenantExample.MvcApplication/Controllers/Tenant1Controller.cs @@ -3,29 +3,28 @@ using MultitenantExample.MvcApplication.WcfMetadataConsumer; using MultitenantExample.MvcApplication.WcfService; -namespace MultitenantExample.MvcApplication.Controllers +namespace MultitenantExample.MvcApplication.Controllers; + +/// +/// Example of a tenant-specific controller for Tenant 1. +/// +/// +/// +/// You have to derive custom controllers from the original controller because +/// you have to register them as the base controller type. +/// +/// +public class Tenant1Controller : HomeController { - /// - /// Example of a tenant-specific controller for Tenant 1. - /// - /// - /// - /// You have to derive custom controllers from the original controller because - /// you have to register them as the base controller type. - /// - /// - public class Tenant1Controller : HomeController + public Tenant1Controller(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy, IMultitenantService standardService, IMetadataConsumer metadataService) + : base(dependency, tenantIdStrategy, standardService, metadataService) { - public Tenant1Controller(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy, IMultitenantService standardService, IMetadataConsumer metadataService) - : base(dependency, tenantIdStrategy, standardService, metadataService) - { - } + } - protected override Models.IndexModel BuildIndexModel() - { - var model = base.BuildIndexModel(); - model.ControllerTypeName += " [This is custom text inserted by the Tenant 1 controller.]"; - return model; - } + protected override Models.IndexModel BuildIndexModel() + { + var model = base.BuildIndexModel(); + model.ControllerTypeName += " [This is custom text inserted by the Tenant 1 controller.]"; + return model; } } diff --git a/src/MultitenantExample.MvcApplication/Controllers/Tenant2Controller.cs b/src/MultitenantExample.MvcApplication/Controllers/Tenant2Controller.cs index 710db16..112fdc6 100644 --- a/src/MultitenantExample.MvcApplication/Controllers/Tenant2Controller.cs +++ b/src/MultitenantExample.MvcApplication/Controllers/Tenant2Controller.cs @@ -3,29 +3,28 @@ using MultitenantExample.MvcApplication.WcfMetadataConsumer; using MultitenantExample.MvcApplication.WcfService; -namespace MultitenantExample.MvcApplication.Controllers +namespace MultitenantExample.MvcApplication.Controllers; + +/// +/// Example of a tenant-specific controller for Tenant 2. +/// +/// +/// +/// You have to derive custom controllers from the original controller because +/// you have to register them as the base controller type. +/// +/// +public class Tenant2Controller : HomeController { - /// - /// Example of a tenant-specific controller for Tenant 2. - /// - /// - /// - /// You have to derive custom controllers from the original controller because - /// you have to register them as the base controller type. - /// - /// - public class Tenant2Controller : HomeController + public Tenant2Controller(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy, IMultitenantService standardService, IMetadataConsumer metadataService) + : base(dependency, tenantIdStrategy, standardService, metadataService) { - public Tenant2Controller(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy, IMultitenantService standardService, IMetadataConsumer metadataService) - : base(dependency, tenantIdStrategy, standardService, metadataService) - { - } + } - protected override Models.IndexModel BuildIndexModel() - { - var model = base.BuildIndexModel(); - model.ControllerTypeName += " [Here is something custom done by the Tenant 2 controller.]"; - return model; - } + protected override Models.IndexModel BuildIndexModel() + { + var model = base.BuildIndexModel(); + model.ControllerTypeName += " [Here is something custom done by the Tenant 2 controller.]"; + return model; } } diff --git a/src/MultitenantExample.MvcApplication/Dependencies/BaseDependency.cs b/src/MultitenantExample.MvcApplication/Dependencies/BaseDependency.cs index 5345c87..4ab154b 100644 --- a/src/MultitenantExample.MvcApplication/Dependencies/BaseDependency.cs +++ b/src/MultitenantExample.MvcApplication/Dependencies/BaseDependency.cs @@ -1,31 +1,30 @@ using System; -namespace MultitenantExample.MvcApplication.Dependencies +namespace MultitenantExample.MvcApplication.Dependencies; + +/// +/// Base class for dependencies. Used simply to avoid redundant code; it's not +/// actually required to have a common derivation chain. +/// +public class BaseDependency : IDependency { /// - /// Base class for dependencies. Used simply to avoid redundant code; it's not - /// actually required to have a common derivation chain. + /// Initializes a new instance of the class. /// - public class BaseDependency : IDependency + public BaseDependency() { - /// - /// Initializes a new instance of the class. - /// - public BaseDependency() - { - InstanceId = Guid.NewGuid(); - } + InstanceId = Guid.NewGuid(); + } - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - public Guid InstanceId - { - get; private set; - } + /// + /// Gets the unique instance ID for the dependency. + /// + /// + /// A that indicates the unique ID for the + /// instance. + /// + public Guid InstanceId + { + get; private set; } } diff --git a/src/MultitenantExample.MvcApplication/Dependencies/DefaultTenantDependency.cs b/src/MultitenantExample.MvcApplication/Dependencies/DefaultTenantDependency.cs index 7fc09cc..f4e175a 100644 --- a/src/MultitenantExample.MvcApplication/Dependencies/DefaultTenantDependency.cs +++ b/src/MultitenantExample.MvcApplication/Dependencies/DefaultTenantDependency.cs @@ -1,9 +1,8 @@ -namespace MultitenantExample.MvcApplication.Dependencies +namespace MultitenantExample.MvcApplication.Dependencies; + +/// +/// Tenant-specific dependency for the default tenant. +/// +public class DefaultTenantDependency : BaseDependency { - /// - /// Tenant-specific dependency for the default tenant. - /// - public class DefaultTenantDependency : BaseDependency - { - } } diff --git a/src/MultitenantExample.MvcApplication/Dependencies/IDependency.cs b/src/MultitenantExample.MvcApplication/Dependencies/IDependency.cs index 9905a33..22bc6bc 100644 --- a/src/MultitenantExample.MvcApplication/Dependencies/IDependency.cs +++ b/src/MultitenantExample.MvcApplication/Dependencies/IDependency.cs @@ -1,23 +1,22 @@ using System; -namespace MultitenantExample.MvcApplication.Dependencies +namespace MultitenantExample.MvcApplication.Dependencies; + +/// +/// Demonstration dependency interface that allows you to inspect the unique +/// ID on a specific resolved instance of the dependency. +/// +public interface IDependency { /// - /// Demonstration dependency interface that allows you to inspect the unique - /// ID on a specific resolved instance of the dependency. + /// Gets the unique instance ID for the dependency. /// - public interface IDependency + /// + /// A that indicates the unique ID for the + /// instance. + /// + Guid InstanceId { - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - Guid InstanceId - { - get; - } + get; } } diff --git a/src/MultitenantExample.MvcApplication/Dependencies/Tenant1Dependency.cs b/src/MultitenantExample.MvcApplication/Dependencies/Tenant1Dependency.cs index 1c980dd..2c5f341 100644 --- a/src/MultitenantExample.MvcApplication/Dependencies/Tenant1Dependency.cs +++ b/src/MultitenantExample.MvcApplication/Dependencies/Tenant1Dependency.cs @@ -1,9 +1,8 @@ -namespace MultitenantExample.MvcApplication.Dependencies +namespace MultitenantExample.MvcApplication.Dependencies; + +/// +/// Tenant-specific dependency for Tenant 1. +/// +public class Tenant1Dependency : BaseDependency { - /// - /// Tenant-specific dependency for Tenant 1. - /// - public class Tenant1Dependency : BaseDependency - { - } } diff --git a/src/MultitenantExample.MvcApplication/Dependencies/Tenant2Dependency.cs b/src/MultitenantExample.MvcApplication/Dependencies/Tenant2Dependency.cs index 9abee16..3d91d98 100644 --- a/src/MultitenantExample.MvcApplication/Dependencies/Tenant2Dependency.cs +++ b/src/MultitenantExample.MvcApplication/Dependencies/Tenant2Dependency.cs @@ -1,9 +1,8 @@ -namespace MultitenantExample.MvcApplication.Dependencies +namespace MultitenantExample.MvcApplication.Dependencies; + +/// +/// Tenant-specific dependency for Tenant 2. +/// +public class Tenant2Dependency : BaseDependency { - /// - /// Tenant-specific dependency for Tenant 2. - /// - public class Tenant2Dependency : BaseDependency - { - } } diff --git a/src/MultitenantExample.MvcApplication/Global.asax.cs b/src/MultitenantExample.MvcApplication/Global.asax.cs index 794daa9..4cffc2e 100644 --- a/src/MultitenantExample.MvcApplication/Global.asax.cs +++ b/src/MultitenantExample.MvcApplication/Global.asax.cs @@ -11,124 +11,123 @@ using MultitenantExample.MvcApplication.WcfMetadataConsumer; using MultitenantExample.MvcApplication.WcfService; -namespace MultitenantExample.MvcApplication +namespace MultitenantExample.MvcApplication; + +/// +/// Global application class for the multitenant MVC example application. +/// +public class MvcApplication : HttpApplication { /// - /// Global application class for the multitenant MVC example application. + /// Registers the application routes with a route collection. + /// + /// + /// The route collection with which to register routes. + /// + /// + /// + /// This is part of standard MVC application setup. + /// + /// + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + + routes.MapRoute( + "Default", // Route name + "{controller}/{action}/{id}", // URL with parameters + new + { + controller = "Home", + action = "Index", + id = UrlParameter.Optional + } // Parameter defaults + ); + + } + + /// + /// Handles the global application startup event. /// - public class MvcApplication : HttpApplication + protected static void Application_Start() { - /// - /// Registers the application routes with a route collection. - /// - /// - /// The route collection with which to register routes. - /// - /// - /// - /// This is part of standard MVC application setup. - /// - /// - public static void RegisterRoutes(RouteCollection routes) + // Register application-level dependencies and controllers. Note that + // we are manually registering controllers rather than all at the same + // time because some of the controllers in this sample application + // are for specific tenants. + var builder = new ContainerBuilder(); + builder.RegisterType(); + builder.RegisterType().As(); + + // Create the tenant ID strategy. Required for multitenant integration. + var tenantIdStrategy = new RequestParameterStrategy(); + + // Adding the tenant ID strategy into the container so controllers + // can display output about the current tenant. + builder.RegisterInstance(tenantIdStrategy).As(); + + // The next couple of registrations - for the channel factory and channel + // to WCF services - show how to consume multitenant WCF services. + + // The service client is not different per tenant because + // the service itself is multitenant - one client for all + // the tenants and the service implementation switches. + builder.Register(c => new ChannelFactory(new BasicHttpBinding(), new EndpointAddress("http://localhost:63578/MultitenantService.svc"))).SingleInstance(); + builder.Register(c => new ChannelFactory(new WSHttpBinding(), new EndpointAddress("http://localhost:63578/MetadataConsumer.svc"))).SingleInstance(); + + // Register an endpoint behavior on the client channel factory that + // will propagate the tenant ID across the wire in a message header. + // On the service side, you'll need to read the header from incoming + // message headers to reconstitute the incoming tenant ID. + builder.Register(c => { - routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); - - routes.MapRoute( - "Default", // Route name - "{controller}/{action}/{id}", // URL with parameters - new - { - controller = "Home", - action = "Index", - id = UrlParameter.Optional - } // Parameter defaults - ); - - } - - /// - /// Handles the global application startup event. - /// - protected void Application_Start() + var factory = c.Resolve>(); + factory.Opening += (sender, args) => factory.Endpoint.Behaviors.Add(new TenantPropagationBehavior(tenantIdStrategy)); + return factory.CreateChannel(); + }).InstancePerRequest(); + builder.Register(c => { - // Register application-level dependencies and controllers. Note that - // we are manually registering controllers rather than all at the same - // time because some of the controllers in this sample application - // are for specific tenants. - var builder = new ContainerBuilder(); - builder.RegisterType(); - builder.RegisterType().As(); - - // Create the tenant ID strategy. Required for multitenant integration. - var tenantIdStrategy = new RequestParameterStrategy(); - - // Adding the tenant ID strategy into the container so controllers - // can display output about the current tenant. - builder.RegisterInstance(tenantIdStrategy).As(); - - // The next couple of registrations - for the channel factory and channel - // to WCF services - show how to consume multitenant WCF services. - - // The service client is not different per tenant because - // the service itself is multitenant - one client for all - // the tenants and the service implementation switches. - builder.Register(c => new ChannelFactory(new BasicHttpBinding(), new EndpointAddress("http://localhost:63578/MultitenantService.svc"))).SingleInstance(); - builder.Register(c => new ChannelFactory(new WSHttpBinding(), new EndpointAddress("http://localhost:63578/MetadataConsumer.svc"))).SingleInstance(); - - // Register an endpoint behavior on the client channel factory that - // will propagate the tenant ID across the wire in a message header. - // On the service side, you'll need to read the header from incoming - // message headers to reconstitute the incoming tenant ID. - builder.Register(c => + var factory = c.Resolve>(); + factory.Opening += (sender, args) => factory.Endpoint.Behaviors.Add(new TenantPropagationBehavior(tenantIdStrategy)); + return factory.CreateChannel(); + }).InstancePerRequest(); + + // Create the multitenant container based on the application + // defaults - here's where the multitenant bits truly come into play. + var mtc = new MultitenantContainer(tenantIdStrategy, builder.Build()); + + // Notice we configure tenant IDs as strings below because the tenant + // identification strategy retrieves string values from the request + // context. To use strongly-typed tenant IDs, create a custom tenant + // identification strategy that returns the appropriate type. + + // Configure overrides for tenant 1 - dependencies, controllers, etc. + mtc.ConfigureTenant("1", + b => { - var factory = c.Resolve>(); - factory.Opening += (sender, args) => factory.Endpoint.Behaviors.Add(new TenantPropagationBehavior(tenantIdStrategy)); - return factory.CreateChannel(); - }).InstancePerRequest(); - builder.Register(c => + b.RegisterType().As().InstancePerDependency(); + b.RegisterType().As(); + }); + + // Configure overrides for tenant 2 - dependencies, controllers, etc. + mtc.ConfigureTenant("2", + b => { - var factory = c.Resolve>(); - factory.Opening += (sender, args) => factory.Endpoint.Behaviors.Add(new TenantPropagationBehavior(tenantIdStrategy)); - return factory.CreateChannel(); - }).InstancePerRequest(); - - // Create the multitenant container based on the application - // defaults - here's where the multitenant bits truly come into play. - var mtc = new MultitenantContainer(tenantIdStrategy, builder.Build()); - - // Notice we configure tenant IDs as strings below because the tenant - // identification strategy retrieves string values from the request - // context. To use strongly-typed tenant IDs, create a custom tenant - // identification strategy that returns the appropriate type. - - // Configure overrides for tenant 1 - dependencies, controllers, etc. - mtc.ConfigureTenant("1", - b => - { - b.RegisterType().As().InstancePerDependency(); - b.RegisterType().As(); - }); - - // Configure overrides for tenant 2 - dependencies, controllers, etc. - mtc.ConfigureTenant("2", - b => - { - b.RegisterType().As().SingleInstance(); - b.RegisterType().As(); - }); - - // Configure overrides for the default tenant. That means the default - // tenant will have some different dependencies than other unconfigured - // tenants. - mtc.ConfigureTenant(null, b => b.RegisterType().As().SingleInstance()); - - // Create the dependency resolver using the - // multitenant container instead of the application container. - DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc)); - - // Perform the standard MVC setup requirements. - AreaRegistration.RegisterAllAreas(); - RegisterRoutes(RouteTable.Routes); - } + b.RegisterType().As().SingleInstance(); + b.RegisterType().As(); + }); + + // Configure overrides for the default tenant. That means the default + // tenant will have some different dependencies than other unconfigured + // tenants. + mtc.ConfigureTenant(null, b => b.RegisterType().As().SingleInstance()); + + // Create the dependency resolver using the + // multitenant container instead of the application container. + DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc)); + + // Perform the standard MVC setup requirements. + AreaRegistration.RegisterAllAreas(); + RegisterRoutes(RouteTable.Routes); } } diff --git a/src/MultitenantExample.MvcApplication/Models/IndexModel.cs b/src/MultitenantExample.MvcApplication/Models/IndexModel.cs index 7476401..63921a2 100644 --- a/src/MultitenantExample.MvcApplication/Models/IndexModel.cs +++ b/src/MultitenantExample.MvcApplication/Models/IndexModel.cs @@ -1,81 +1,80 @@ using System; -namespace MultitenantExample.MvcApplication.Models +namespace MultitenantExample.MvcApplication.Models; + +/// +/// Model used on the main index view to display information about the current +/// dependency settings. +/// +public class IndexModel { /// - /// Model used on the main index view to display information about the current - /// dependency settings. + /// Gets or sets the tenant ID to display. /// - public class IndexModel + /// + /// A that represents the current tenant ID. + /// + public object? TenantId { - /// - /// Gets or sets the tenant ID to display. - /// - /// - /// A that represents the current tenant ID. - /// - public object TenantId - { - get; set; - } + get; set; + } - /// - /// Gets or sets the controller type name. - /// - /// - /// A with the controller type name to display. - /// - public string ControllerTypeName - { - get; set; - } + /// + /// Gets or sets the controller type name. + /// + /// + /// A with the controller type name to display. + /// + public string? ControllerTypeName + { + get; set; + } - /// - /// Gets or sets the dependency type name. - /// - /// - /// A with the dependency type name to display. - /// - public string DependencyTypeName - { - get; set; - } + /// + /// Gets or sets the dependency type name. + /// + /// + /// A with the dependency type name to display. + /// + public string? DependencyTypeName + { + get; set; + } - /// - /// Gets or sets the dependency instance ID. - /// - /// - /// A that indicates the unique ID for the dependency instance. - /// - public Guid DependencyInstanceId - { - get; set; - } + /// + /// Gets or sets the dependency instance ID. + /// + /// + /// A that indicates the unique ID for the dependency instance. + /// + public Guid DependencyInstanceId + { + get; set; + } - /// - /// Gets or sets the WCF service information for the service that consumes - /// a metadata buddy class. - /// - /// - /// A - /// containing information retrieved from the multitenant WCF service. - /// - public WcfMetadataConsumer.GetServiceInfoResponse MetadataServiceInfo - { - get; set; - } + /// + /// Gets or sets the WCF service information for the service that consumes + /// a metadata buddy class. + /// + /// + /// A + /// containing information retrieved from the multitenant WCF service. + /// + public WcfMetadataConsumer.GetServiceInfoResponse? MetadataServiceInfo + { + get; set; + } - /// - /// Gets or sets the WCF service information for the service that doesn't - /// use a metadata buddy class. - /// - /// - /// A - /// containing information retrieved from the multitenant WCF service. - /// - public WcfService.GetServiceInfoResponse StandardServiceInfo - { - get; set; - } + /// + /// Gets or sets the WCF service information for the service that doesn't + /// use a metadata buddy class. + /// + /// + /// A + /// containing information retrieved from the multitenant WCF service. + /// + public WcfService.GetServiceInfoResponse? StandardServiceInfo + { + get; set; } } diff --git a/src/MultitenantExample.MvcApplication/RequestParameterStrategy.cs b/src/MultitenantExample.MvcApplication/RequestParameterStrategy.cs index 986ddbb..a93ddcc 100644 --- a/src/MultitenantExample.MvcApplication/RequestParameterStrategy.cs +++ b/src/MultitenantExample.MvcApplication/RequestParameterStrategy.cs @@ -1,28 +1,27 @@ using System.Web; using Autofac.Multitenant; -namespace MultitenantExample.MvcApplication +namespace MultitenantExample.MvcApplication; + +public class RequestParameterStrategy : ITenantIdentificationStrategy { - public class RequestParameterStrategy : ITenantIdentificationStrategy + public bool TryIdentifyTenant(out object? tenantId) { - public bool TryIdentifyTenant(out object tenantId) + // This is an EXAMPLE ONLY and is NOT RECOMMENDED. + tenantId = null; + try { - // This is an EXAMPLE ONLY and is NOT RECOMMENDED. - tenantId = null; - try - { - var context = HttpContext.Current; - if (context != null && context.Request != null) - { - tenantId = context.Request.Params["tenant"]; - } - } - catch (HttpException) + var context = HttpContext.Current; + if (context != null && context.Request != null) { - // Happens at app startup in IIS 7.0 + tenantId = context.Request.Params["tenant"]; } - - return tenantId != null; } + catch (HttpException) + { + // Happens at app startup in IIS 7.0 + } + + return tenantId != null; } } diff --git a/src/MultitenantExample.WcfService/Dependencies/BaseDependency.cs b/src/MultitenantExample.WcfService/Dependencies/BaseDependency.cs index 91b8525..3a30893 100644 --- a/src/MultitenantExample.WcfService/Dependencies/BaseDependency.cs +++ b/src/MultitenantExample.WcfService/Dependencies/BaseDependency.cs @@ -1,31 +1,30 @@ using System; -namespace MultitenantExample.WcfService.Dependencies +namespace MultitenantExample.WcfService.Dependencies; + +/// +/// Base class for dependencies. Used simply to avoid redundant code; it's not +/// actually required to have a common derivation chain. +/// +public class BaseDependency : IDependency { /// - /// Base class for dependencies. Used simply to avoid redundant code; it's not - /// actually required to have a common derivation chain. + /// Initializes a new instance of the class. /// - public class BaseDependency : IDependency + public BaseDependency() { - /// - /// Initializes a new instance of the class. - /// - public BaseDependency() - { - InstanceId = Guid.NewGuid(); - } + InstanceId = Guid.NewGuid(); + } - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - public Guid InstanceId - { - get; private set; - } + /// + /// Gets the unique instance ID for the dependency. + /// + /// + /// A that indicates the unique ID for the + /// instance. + /// + public Guid InstanceId + { + get; private set; } } diff --git a/src/MultitenantExample.WcfService/Dependencies/DefaultTenantDependency.cs b/src/MultitenantExample.WcfService/Dependencies/DefaultTenantDependency.cs index cfc817e..207f247 100644 --- a/src/MultitenantExample.WcfService/Dependencies/DefaultTenantDependency.cs +++ b/src/MultitenantExample.WcfService/Dependencies/DefaultTenantDependency.cs @@ -1,9 +1,8 @@ -namespace MultitenantExample.WcfService.Dependencies +namespace MultitenantExample.WcfService.Dependencies; + +/// +/// Tenant-specific dependency for the default tenant. +/// +public class DefaultTenantDependency : BaseDependency { - /// - /// Tenant-specific dependency for the default tenant. - /// - public class DefaultTenantDependency : BaseDependency - { - } } diff --git a/src/MultitenantExample.WcfService/Dependencies/IDependency.cs b/src/MultitenantExample.WcfService/Dependencies/IDependency.cs index 780793d..0a3e9ae 100644 --- a/src/MultitenantExample.WcfService/Dependencies/IDependency.cs +++ b/src/MultitenantExample.WcfService/Dependencies/IDependency.cs @@ -1,23 +1,22 @@ using System; -namespace MultitenantExample.WcfService.Dependencies +namespace MultitenantExample.WcfService.Dependencies; + +/// +/// Demonstration dependency interface that allows you to inspect the unique +/// ID on a specific resolved instance of the dependency. +/// +public interface IDependency { /// - /// Demonstration dependency interface that allows you to inspect the unique - /// ID on a specific resolved instance of the dependency. + /// Gets the unique instance ID for the dependency. /// - public interface IDependency + /// + /// A that indicates the unique ID for the + /// instance. + /// + Guid InstanceId { - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - Guid InstanceId - { - get; - } + get; } } diff --git a/src/MultitenantExample.WcfService/Dependencies/Tenant1Dependency.cs b/src/MultitenantExample.WcfService/Dependencies/Tenant1Dependency.cs index 9a694bf..6acf8e9 100644 --- a/src/MultitenantExample.WcfService/Dependencies/Tenant1Dependency.cs +++ b/src/MultitenantExample.WcfService/Dependencies/Tenant1Dependency.cs @@ -1,9 +1,8 @@ -namespace MultitenantExample.WcfService.Dependencies +namespace MultitenantExample.WcfService.Dependencies; + +/// +/// Tenant-specific dependency for Tenant 1. +/// +public class Tenant1Dependency : BaseDependency { - /// - /// Tenant-specific dependency for Tenant 1. - /// - public class Tenant1Dependency : BaseDependency - { - } } diff --git a/src/MultitenantExample.WcfService/Dependencies/Tenant2Dependency.cs b/src/MultitenantExample.WcfService/Dependencies/Tenant2Dependency.cs index 0c4d42f..a6bdc2b 100644 --- a/src/MultitenantExample.WcfService/Dependencies/Tenant2Dependency.cs +++ b/src/MultitenantExample.WcfService/Dependencies/Tenant2Dependency.cs @@ -1,9 +1,8 @@ -namespace MultitenantExample.WcfService.Dependencies +namespace MultitenantExample.WcfService.Dependencies; + +/// +/// Tenant-specific dependency for Tenant 2. +/// +public class Tenant2Dependency : BaseDependency { - /// - /// Tenant-specific dependency for Tenant 2. - /// - public class Tenant2Dependency : BaseDependency - { - } } diff --git a/src/MultitenantExample.WcfService/GetServiceInfoResponse.cs b/src/MultitenantExample.WcfService/GetServiceInfoResponse.cs index 3014865..7dc7222 100644 --- a/src/MultitenantExample.WcfService/GetServiceInfoResponse.cs +++ b/src/MultitenantExample.WcfService/GetServiceInfoResponse.cs @@ -1,60 +1,59 @@ using System; using System.ServiceModel; -namespace MultitenantExample.WcfService +namespace MultitenantExample.WcfService; + +/// +/// Response message contract for the service info operation. +/// +[MessageContract] +public class GetServiceInfoResponse { /// - /// Response message contract for the service info operation. + /// Gets or sets the tenant ID handling the request. /// - [MessageContract] - public class GetServiceInfoResponse + /// + /// A that represents the current tenant ID. + /// + [MessageBodyMember] + public string? TenantId { - /// - /// Gets or sets the tenant ID handling the request. - /// - /// - /// A that represents the current tenant ID. - /// - [MessageBodyMember] - public string TenantId - { - get; set; - } + get; set; + } - /// - /// Gets or sets the service implementation type name. - /// - /// - /// A with the service implementation type name to display. - /// - [MessageBodyMember] - public string ServiceImplementationTypeName - { - get; set; - } + /// + /// Gets or sets the service implementation type name. + /// + /// + /// A with the service implementation type name to display. + /// + [MessageBodyMember] + public string? ServiceImplementationTypeName + { + get; set; + } - /// - /// /// Gets or sets the dependency type name. - /// - /// - /// A with the dependency type name to display. - /// - [MessageBodyMember] - public string DependencyTypeName - { - get; set; - } + /// + /// /// Gets or sets the dependency type name. + /// + /// + /// A with the dependency type name to display. + /// + [MessageBodyMember] + public string? DependencyTypeName + { + get; set; + } - /// - /// Gets or sets the dependency instance ID. - /// - /// - /// A that indicates the unique ID for the dependency instance. - /// - [MessageBodyMember] - public Guid DependencyInstanceId - { - get; set; - } + /// + /// Gets or sets the dependency instance ID. + /// + /// + /// A that indicates the unique ID for the dependency instance. + /// + [MessageBodyMember] + public Guid DependencyInstanceId + { + get; set; } } diff --git a/src/MultitenantExample.WcfService/Global.asax.cs b/src/MultitenantExample.WcfService/Global.asax.cs index 3bfb8f0..72585d5 100644 --- a/src/MultitenantExample.WcfService/Global.asax.cs +++ b/src/MultitenantExample.WcfService/Global.asax.cs @@ -6,97 +6,96 @@ using MultitenantExample.WcfService.Dependencies; using MultitenantExample.WcfService.ServiceImplementations; -namespace MultitenantExample.WcfService +namespace MultitenantExample.WcfService; + +/// +/// Global application class for the multitenant WCF example application. +/// +/// +/// +/// It is easiest to see this application in action from the MVC example. +/// The MVC example makes use of this service and displays the information, +/// illustrating a complete multitenant system in action. +/// +/// +public class Global : System.Web.HttpApplication { /// - /// Global application class for the multitenant WCF example application. + /// Handles the global application startup event. /// - /// - /// - /// It is easiest to see this application in action from the MVC example. - /// The MVC example makes use of this service and displays the information, - /// illustrating a complete multitenant system in action. - /// - /// - public class Global : System.Web.HttpApplication + protected void Application_Start(object sender, EventArgs e) { - /// - /// Handles the global application startup event. - /// - protected void Application_Start(object sender, EventArgs e) - { - // Create the tenant ID strategy. Required for multitenant integration. - var tenantIdStrategy = new OperationContextTenantIdentificationStrategy(); + // Create the tenant ID strategy. Required for multitenant integration. + var tenantIdStrategy = new OperationContextTenantIdentificationStrategy(); - // Register application-level dependencies and service implementations. - // Note that we are registering the services as the interface type - // because the .svc files refer to the interfaces. We could potentially - // use named service types as well. - var builder = new ContainerBuilder(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); + // Register application-level dependencies and service implementations. + // Note that we are registering the services as the interface type + // because the .svc files refer to the interfaces. We could potentially + // use named service types as well. + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); - // Adding the tenant ID strategy into the container so services - // can return output about the current tenant. - builder.RegisterInstance(tenantIdStrategy).As(); + // Adding the tenant ID strategy into the container so services + // can return output about the current tenant. + builder.RegisterInstance(tenantIdStrategy).As(); - // Create the multitenant container based on the application - // defaults - here's where the multitenant bits truly come into play. - var mtc = new MultitenantContainer(tenantIdStrategy, builder.Build()); + // Create the multitenant container based on the application + // defaults - here's where the multitenant bits truly come into play. + var mtc = new MultitenantContainer(tenantIdStrategy, builder.Build()); - // Notice we configure tenant IDs as strings below because the tenant - // identification strategy retrieves string values from the message - // headers. + // Notice we configure tenant IDs as strings below because the tenant + // identification strategy retrieves string values from the message + // headers. - // Configure overrides for tenant 1 - dependencies, service implementations, etc. - mtc.ConfigureTenant("1", - b => - { - b.RegisterType().As().InstancePerDependency(); - b.RegisterType().As(); - b.RegisterType().As(); - }); + // Configure overrides for tenant 1 - dependencies, service implementations, etc. + mtc.ConfigureTenant("1", + b => + { + b.RegisterType().As().InstancePerDependency(); + b.RegisterType().As(); + b.RegisterType().As(); + }); - // Configure overrides for tenant 2 - dependencies, service implementations, etc. - mtc.ConfigureTenant("2", - b => - { - b.RegisterType().As().SingleInstance(); - b.RegisterType().As(); - b.RegisterType().As(); - }); + // Configure overrides for tenant 2 - dependencies, service implementations, etc. + mtc.ConfigureTenant("2", + b => + { + b.RegisterType().As().SingleInstance(); + b.RegisterType().As(); + b.RegisterType().As(); + }); - // Configure overrides for the default tenant. That means the default - // tenant will have some different dependencies than other unconfigured - // tenants. - mtc.ConfigureTenant(null, b => b.RegisterType().As().SingleInstance()); + // Configure overrides for the default tenant. That means the default + // tenant will have some different dependencies than other unconfigured + // tenants. + mtc.ConfigureTenant(null, b => b.RegisterType().As().SingleInstance()); - // Multitenant service hosting requires use of a different service implementation - // data provider that will allow you to define a metadata buddy class that isn't - // tenant-specific. - AutofacHostFactory.ServiceImplementationDataProvider = new MultitenantServiceImplementationDataProvider(); + // Multitenant service hosting requires use of a different service implementation + // data provider that will allow you to define a metadata buddy class that isn't + // tenant-specific. + AutofacHostFactory.ServiceImplementationDataProvider = new MultitenantServiceImplementationDataProvider(); - // Add a behavior to service hosts that get created so incoming messages - // get inspected and the tenant ID can be parsed from message headers. - // For multitenancy to work, you need to know for which tenant a - // given request is being made. In this case, the incoming message headers - // expect to see a string for the tenant ID; if your tenant ID coming - // from clients is different, change that here. - AutofacHostFactory.HostConfigurationAction = - host => - host.Opening += (s, args) => - host.Description.Behaviors.Add(new TenantPropagationBehavior(tenantIdStrategy)); + // Add a behavior to service hosts that get created so incoming messages + // get inspected and the tenant ID can be parsed from message headers. + // For multitenancy to work, you need to know for which tenant a + // given request is being made. In this case, the incoming message headers + // expect to see a string for the tenant ID; if your tenant ID coming + // from clients is different, change that here. + AutofacHostFactory.HostConfigurationAction = + host => + host.Opening += (s, args) => + host.Description.Behaviors.Add(new TenantPropagationBehavior(tenantIdStrategy)); - // Finally, set the host factory application container on the multitenant - // WCF host to a multitenant container. This is similar to standard - // Autofac WCF integration. - AutofacHostFactory.Container = mtc; + // Finally, set the host factory application container on the multitenant + // WCF host to a multitenant container. This is similar to standard + // Autofac WCF integration. + AutofacHostFactory.Container = mtc; - // Note that the .svc file for your service needs to use the - // Autofac.Extras.Multitenant.Wcf.AutofacServiceHostFactory or - // Autofac.Extras.Multitenant.Wcf.AutofacWebServiceHostFactory rather - // than the standard Autofac host factories. - } + // Note that the .svc file for your service needs to use the + // Autofac.Extras.Multitenant.Wcf.AutofacServiceHostFactory or + // Autofac.Extras.Multitenant.Wcf.AutofacWebServiceHostFactory rather + // than the standard Autofac host factories. } } diff --git a/src/MultitenantExample.WcfService/IMetadataConsumer.cs b/src/MultitenantExample.WcfService/IMetadataConsumer.cs index 6f496d3..7871af1 100644 --- a/src/MultitenantExample.WcfService/IMetadataConsumer.cs +++ b/src/MultitenantExample.WcfService/IMetadataConsumer.cs @@ -1,13 +1,12 @@ using System.ServiceModel; using Autofac.Multitenant.Wcf; -namespace MultitenantExample.WcfService +namespace MultitenantExample.WcfService; + +[ServiceContract] +[ServiceMetadataType(typeof(MetadataConsumerBuddyClass))] +public interface IMetadataConsumer { - [ServiceContract] - [ServiceMetadataType(typeof(MetadataConsumerBuddyClass))] - public interface IMetadataConsumer - { - [OperationContract] - GetServiceInfoResponse GetServiceInfo(); - } + [OperationContract] + GetServiceInfoResponse GetServiceInfo(); } diff --git a/src/MultitenantExample.WcfService/IMultitenantService.cs b/src/MultitenantExample.WcfService/IMultitenantService.cs index 01b742e..8a83de4 100644 --- a/src/MultitenantExample.WcfService/IMultitenantService.cs +++ b/src/MultitenantExample.WcfService/IMultitenantService.cs @@ -1,11 +1,10 @@ using System.ServiceModel; -namespace MultitenantExample.WcfService +namespace MultitenantExample.WcfService; + +[ServiceContract] +public interface IMultitenantService { - [ServiceContract] - public interface IMultitenantService - { - [OperationContract] - GetServiceInfoResponse GetServiceInfo(); - } + [OperationContract] + GetServiceInfoResponse GetServiceInfo(); } diff --git a/src/MultitenantExample.WcfService/MetadataConsumerBuddyClass.cs b/src/MultitenantExample.WcfService/MetadataConsumerBuddyClass.cs index 32e7168..c555392 100644 --- a/src/MultitenantExample.WcfService/MetadataConsumerBuddyClass.cs +++ b/src/MultitenantExample.WcfService/MetadataConsumerBuddyClass.cs @@ -1,12 +1,11 @@ using System.ServiceModel; -namespace MultitenantExample.WcfService +namespace MultitenantExample.WcfService; + +// Specifying a ConfigurationName on a metadata buddy class allows you to +// set configuration in web.config (or app.config) and have a nice, friendly, +// predictable configuration name for your service element. +[ServiceBehavior(ConfigurationName = "MultitenantExample.WcfService.IMetadataConsumer")] +public class MetadataConsumerBuddyClass { - // Specifying a ConfigurationName on a metadata buddy class allows you to - // set configuration in web.config (or app.config) and have a nice, friendly, - // predictable configuration name for your service element. - [ServiceBehavior(ConfigurationName = "MultitenantExample.WcfService.IMetadataConsumer")] - public class MetadataConsumerBuddyClass - { - } } diff --git a/src/MultitenantExample.WcfService/ServiceImplementations/BaseImplementation.cs b/src/MultitenantExample.WcfService/ServiceImplementations/BaseImplementation.cs index 990631a..709e6de 100644 --- a/src/MultitenantExample.WcfService/ServiceImplementations/BaseImplementation.cs +++ b/src/MultitenantExample.WcfService/ServiceImplementations/BaseImplementation.cs @@ -1,29 +1,28 @@ using Autofac.Multitenant; using MultitenantExample.WcfService.Dependencies; -namespace MultitenantExample.WcfService.ServiceImplementations +namespace MultitenantExample.WcfService.ServiceImplementations; + +public class BaseImplementation : IMultitenantService, IMetadataConsumer { - public class BaseImplementation : IMultitenantService, IMetadataConsumer + public BaseImplementation(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy) { - public BaseImplementation(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy) - { - Dependency = dependency; - TenantIdentificationStrategy = tenantIdStrategy; - } + Dependency = dependency; + TenantIdentificationStrategy = tenantIdStrategy; + } - public IDependency Dependency - { - get; set; - } + public IDependency Dependency + { + get; set; + } - public ITenantIdentificationStrategy TenantIdentificationStrategy - { - get; set; - } + public ITenantIdentificationStrategy TenantIdentificationStrategy + { + get; set; + } - public GetServiceInfoResponse GetServiceInfo() - { - return ServiceInfoBuilder.Build(this, Dependency, TenantIdentificationStrategy); - } + public GetServiceInfoResponse GetServiceInfo() + { + return ServiceInfoBuilder.Build(this, Dependency, TenantIdentificationStrategy); } } diff --git a/src/MultitenantExample.WcfService/ServiceImplementations/ServiceInfoBuilder.cs b/src/MultitenantExample.WcfService/ServiceImplementations/ServiceInfoBuilder.cs index 6662ec5..4515d4d 100644 --- a/src/MultitenantExample.WcfService/ServiceImplementations/ServiceInfoBuilder.cs +++ b/src/MultitenantExample.WcfService/ServiceImplementations/ServiceInfoBuilder.cs @@ -1,52 +1,51 @@ using Autofac.Multitenant; using MultitenantExample.WcfService.Dependencies; -namespace MultitenantExample.WcfService.ServiceImplementations +namespace MultitenantExample.WcfService.ServiceImplementations; + +/// +/// Common logic for building a service info response. +/// +/// +/// +/// This builder class is used instead of an +/// inheritance hierarchy to illustrate that service implementations only +/// have to implement the same service contract; they don't need to share +/// an inheritance chain. +/// +/// +public static class ServiceInfoBuilder { /// - /// Common logic for building a service info response. + /// Builds a service info response. /// - /// - /// - /// This builder class is used instead of an - /// inheritance hierarchy to illustrate that service implementations only - /// have to implement the same service contract; they don't need to share - /// an inheritance chain. - /// - /// - public static class ServiceInfoBuilder + /// + /// The service implementation that will be returning the response. + /// + /// + /// The dependency that was provided to the service implementation on construction. + /// + /// + /// The tenant ID strategy. + /// + /// + /// A populated service info response. + /// + public static GetServiceInfoResponse Build(IMultitenantService serviceImplementation, IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy) { - /// - /// Builds a service info response. - /// - /// - /// The service implementation that will be returning the response. - /// - /// - /// The dependency that was provided to the service implementation on construction. - /// - /// - /// The tenant ID strategy. - /// - /// - /// A populated service info response. - /// - public static GetServiceInfoResponse Build(IMultitenantService serviceImplementation, IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy) + var success = tenantIdStrategy.TryIdentifyTenant(out var tenantId); + if (!success || tenantId == null) { - var success = tenantIdStrategy.TryIdentifyTenant(out var tenantId); - if (!success || tenantId == null) - { - tenantId = "[Default Tenant]"; - } - - var response = new GetServiceInfoResponse() - { - ServiceImplementationTypeName = serviceImplementation.GetType().Name, - DependencyInstanceId = dependency.InstanceId, - DependencyTypeName = dependency.GetType().Name, - TenantId = tenantId.ToString() - }; - return response; + tenantId = "[Default Tenant]"; } + + var response = new GetServiceInfoResponse() + { + ServiceImplementationTypeName = serviceImplementation.GetType().Name, + DependencyInstanceId = dependency.InstanceId, + DependencyTypeName = dependency.GetType().Name, + TenantId = tenantId.ToString() + }; + return response; } } diff --git a/src/MultitenantExample.WcfService/ServiceImplementations/Tenant1Implementation.cs b/src/MultitenantExample.WcfService/ServiceImplementations/Tenant1Implementation.cs index cdcf7a7..5f5bb9d 100644 --- a/src/MultitenantExample.WcfService/ServiceImplementations/Tenant1Implementation.cs +++ b/src/MultitenantExample.WcfService/ServiceImplementations/Tenant1Implementation.cs @@ -1,31 +1,30 @@ using Autofac.Multitenant; using MultitenantExample.WcfService.Dependencies; -namespace MultitenantExample.WcfService.ServiceImplementations +namespace MultitenantExample.WcfService.ServiceImplementations; + +public class Tenant1Implementation : IMultitenantService, IMetadataConsumer { - public class Tenant1Implementation : IMultitenantService, IMetadataConsumer + public Tenant1Implementation(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy) { - public Tenant1Implementation(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy) - { - Dependency = dependency; - TenantIdentificationStrategy = tenantIdStrategy; - } + Dependency = dependency; + TenantIdentificationStrategy = tenantIdStrategy; + } - public IDependency Dependency - { - get; set; - } + public IDependency Dependency + { + get; set; + } - public ITenantIdentificationStrategy TenantIdentificationStrategy - { - get; set; - } + public ITenantIdentificationStrategy TenantIdentificationStrategy + { + get; set; + } - public GetServiceInfoResponse GetServiceInfo() - { - var response = ServiceInfoBuilder.Build(this, Dependency, TenantIdentificationStrategy); - response.ServiceImplementationTypeName += " [Custom value from Tenant 1 service imp.]"; - return response; - } + public GetServiceInfoResponse GetServiceInfo() + { + var response = ServiceInfoBuilder.Build(this, Dependency, TenantIdentificationStrategy); + response.ServiceImplementationTypeName += " [Custom value from Tenant 1 service imp.]"; + return response; } } diff --git a/src/MultitenantExample.WcfService/ServiceImplementations/Tenant2Implementation.cs b/src/MultitenantExample.WcfService/ServiceImplementations/Tenant2Implementation.cs index 3c46696..10d951d 100644 --- a/src/MultitenantExample.WcfService/ServiceImplementations/Tenant2Implementation.cs +++ b/src/MultitenantExample.WcfService/ServiceImplementations/Tenant2Implementation.cs @@ -1,31 +1,30 @@ using Autofac.Multitenant; using MultitenantExample.WcfService.Dependencies; -namespace MultitenantExample.WcfService.ServiceImplementations +namespace MultitenantExample.WcfService.ServiceImplementations; + +public class Tenant2Implementation : IMultitenantService, IMetadataConsumer { - public class Tenant2Implementation : IMultitenantService, IMetadataConsumer + public Tenant2Implementation(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy) { - public Tenant2Implementation(IDependency dependency, ITenantIdentificationStrategy tenantIdStrategy) - { - Dependency = dependency; - TenantIdentificationStrategy = tenantIdStrategy; - } + Dependency = dependency; + TenantIdentificationStrategy = tenantIdStrategy; + } - public IDependency Dependency - { - get; set; - } + public IDependency Dependency + { + get; set; + } - public ITenantIdentificationStrategy TenantIdentificationStrategy - { - get; set; - } + public ITenantIdentificationStrategy TenantIdentificationStrategy + { + get; set; + } - public GetServiceInfoResponse GetServiceInfo() - { - var response = ServiceInfoBuilder.Build(this, Dependency, TenantIdentificationStrategy); - response.ServiceImplementationTypeName += " [Tenant 2 service imp custom value here.]"; - return response; - } + public GetServiceInfoResponse GetServiceInfo() + { + var response = ServiceInfoBuilder.Build(this, Dependency, TenantIdentificationStrategy); + response.ServiceImplementationTypeName += " [Tenant 2 service imp custom value here.]"; + return response; } } diff --git a/src/MvcExample/Controllers/HomeController.cs b/src/MvcExample/Controllers/HomeController.cs index 85f0340..863b325 100644 --- a/src/MvcExample/Controllers/HomeController.cs +++ b/src/MvcExample/Controllers/HomeController.cs @@ -3,40 +3,39 @@ using MvcExample.HostFactoryService; using MvcExample.Models; -namespace MvcExample.Controllers +namespace MvcExample.Controllers; + +public class HomeController : Controller { - public class HomeController : Controller + public HomeController(IService serviceClient) { - public HomeController(IService serviceClient) - { - ServiceClient = serviceClient; - } + ServiceClient = serviceClient; + } - public IService ServiceClient - { - get; private set; - } + public IService ServiceClient + { + get; private set; + } - public ActionResult About() - { - return View(); - } + public ActionResult About() + { + return View(); + } - [CustomActionFilter] - public async Task Index() - { - var serviceInfo = await ServiceClient.GetServiceInfoAsync(new GetServiceInfoRequest()); + [CustomActionFilter] + public async Task Index() + { + var serviceInfo = await ServiceClient.GetServiceInfoAsync(new GetServiceInfoRequest()); - var model = new DependencyValueModel - { - // Comes from a dependency in the custom action filter. - FilterValue = (long)HttpContext.Items["filterValue"], + var model = new DependencyValueModel + { + // Comes from a dependency in the custom action filter. + FilterValue = (long)HttpContext.Items["filterValue"], - // Comes from a dependency injected into the WCF service. - WcfServiceDependencyId = serviceInfo.DependencyInstanceId - }; + // Comes from a dependency injected into the WCF service. + WcfServiceDependencyId = serviceInfo.DependencyInstanceId + }; - return View(model); - } + return View(model); } } diff --git a/src/MvcExample/CustomActionFilterAttribute.cs b/src/MvcExample/CustomActionFilterAttribute.cs index a5e2641..63ccba7 100644 --- a/src/MvcExample/CustomActionFilterAttribute.cs +++ b/src/MvcExample/CustomActionFilterAttribute.cs @@ -1,20 +1,19 @@ using System.Web.Mvc; using MvcExample.Dependencies; -namespace MvcExample +namespace MvcExample; + +public class CustomActionFilterAttribute : ActionFilterAttribute { - public class CustomActionFilterAttribute : ActionFilterAttribute + public IFilterDependency? Dependency { - public IFilterDependency Dependency - { - get; set; - } + get; set; + } - public override void OnActionExecuting(ActionExecutingContext filterContext) - { - // This filter adds a value from the dependency to the current context - // so the controller can grab it and pass it to the view. - filterContext.HttpContext.Items["filterValue"] = Dependency.CurrentTicks; - } + public override void OnActionExecuting(ActionExecutingContext filterContext) + { + // This filter adds a value from the dependency to the current context + // so the controller can grab it and pass it to the view. + filterContext.HttpContext.Items["filterValue"] = Dependency.CurrentTicks; } } diff --git a/src/MvcExample/CustomViewPage.cs b/src/MvcExample/CustomViewPage.cs index 34c0f53..e2214fd 100644 --- a/src/MvcExample/CustomViewPage.cs +++ b/src/MvcExample/CustomViewPage.cs @@ -1,17 +1,16 @@ using System.Web.Mvc; using MvcExample.Dependencies; -namespace MvcExample +namespace MvcExample; + +/// +/// Custom view page base class to illustrate view injection. +/// +/// +public abstract class CustomViewPage : WebViewPage { - /// - /// Custom view page base class to illustrate view injection. - /// - /// - public abstract class CustomViewPage : WebViewPage + public IViewDependency? Dependency { - public IViewDependency Dependency - { - get; set; - } + get; set; } } diff --git a/src/MvcExample/Dependencies/FilterDependency.cs b/src/MvcExample/Dependencies/FilterDependency.cs index 19408d6..2633aa0 100644 --- a/src/MvcExample/Dependencies/FilterDependency.cs +++ b/src/MvcExample/Dependencies/FilterDependency.cs @@ -1,25 +1,24 @@ using System; -namespace MvcExample.Dependencies +namespace MvcExample.Dependencies; + +/// +/// Implementation of a simple dependency to inject into a filter. +/// +/// +public class FilterDependency : IFilterDependency { /// - /// Implementation of a simple dependency to inject into a filter. + /// Gets the current date and time as ticks. /// - /// - public class FilterDependency : IFilterDependency + /// + /// An with the current date and time as ticks. + /// + public long CurrentTicks { - /// - /// Gets the current date and time as ticks. - /// - /// - /// An with the current date and time as ticks. - /// - public long CurrentTicks + get { - get - { - return DateTime.UtcNow.Ticks; - } + return DateTime.UtcNow.Ticks; } } } diff --git a/src/MvcExample/Dependencies/IFilterDependency.cs b/src/MvcExample/Dependencies/IFilterDependency.cs index ab917d8..37f6638 100644 --- a/src/MvcExample/Dependencies/IFilterDependency.cs +++ b/src/MvcExample/Dependencies/IFilterDependency.cs @@ -1,19 +1,18 @@ -namespace MvcExample.Dependencies +namespace MvcExample.Dependencies; + +/// +/// Simple dependency to show injection into an action filter. +/// +public interface IFilterDependency { /// - /// Simple dependency to show injection into an action filter. + /// Gets the current date and time as ticks. /// - public interface IFilterDependency + /// + /// An with the current date and time as ticks. + /// + long CurrentTicks { - /// - /// Gets the current date and time as ticks. - /// - /// - /// An with the current date and time as ticks. - /// - long CurrentTicks - { - get; - } + get; } } diff --git a/src/MvcExample/Dependencies/IViewDependency.cs b/src/MvcExample/Dependencies/IViewDependency.cs index 1335bb7..60f85e6 100644 --- a/src/MvcExample/Dependencies/IViewDependency.cs +++ b/src/MvcExample/Dependencies/IViewDependency.cs @@ -1,22 +1,21 @@ using System; -namespace MvcExample.Dependencies +namespace MvcExample.Dependencies; + +/// +/// Simple dependency to show injection into a Razor view. +/// +public interface IViewDependency { /// - /// Simple dependency to show injection into a Razor view. + /// Gets the unique instance ID for the dependency. /// - public interface IViewDependency + /// + /// A that indicates the unique ID for the + /// instance. + /// + Guid InstanceId { - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - Guid InstanceId - { - get; - } + get; } } diff --git a/src/MvcExample/Dependencies/ViewDependency.cs b/src/MvcExample/Dependencies/ViewDependency.cs index d4f99d2..03eafcb 100644 --- a/src/MvcExample/Dependencies/ViewDependency.cs +++ b/src/MvcExample/Dependencies/ViewDependency.cs @@ -1,31 +1,30 @@ using System; -namespace MvcExample.Dependencies +namespace MvcExample.Dependencies; + +/// +/// Implementation of a simple dependency to inject into a view. +/// +/// +public class ViewDependency : IViewDependency { /// - /// Implementation of a simple dependency to inject into a view. + /// Initializes a new instance of the class. /// - /// - public class ViewDependency : IViewDependency + public ViewDependency() { - /// - /// Initializes a new instance of the class. - /// - public ViewDependency() - { - InstanceId = Guid.NewGuid(); - } + InstanceId = Guid.NewGuid(); + } - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - public Guid InstanceId - { - get; private set; - } + /// + /// Gets the unique instance ID for the dependency. + /// + /// + /// A that indicates the unique ID for the + /// instance. + /// + public Guid InstanceId + { + get; private set; } } diff --git a/src/MvcExample/Global.asax.cs b/src/MvcExample/Global.asax.cs index 449d910..b18e238 100644 --- a/src/MvcExample/Global.asax.cs +++ b/src/MvcExample/Global.asax.cs @@ -8,84 +8,83 @@ using MvcExample.Dependencies; using MvcExample.HostFactoryService; -namespace MvcExample +namespace MvcExample; + +public class MvcApplication : System.Web.HttpApplication { - public class MvcApplication : System.Web.HttpApplication + public static void RegisterBundles(BundleCollection bundles) { - public static void RegisterBundles(BundleCollection bundles) - { - bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/Site.css")); - } + bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/Site.css")); + } - public static void RegisterGlobalFilters(GlobalFilterCollection filters) - { - filters.Add(new HandleErrorAttribute()); - } + public static void RegisterGlobalFilters(GlobalFilterCollection filters) + { + filters.Add(new HandleErrorAttribute()); + } - public static void RegisterRoutes(RouteCollection routes) - { - routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); - routes.MapRoute( - name: "Default", - url: "{controller}/{action}/{id}", - defaults: new - { - controller = "Home", - action = "Index", - id = UrlParameter.Optional - } - ); - } + routes.MapRoute( + name: "Default", + url: "{controller}/{action}/{id}", + defaults: new + { + controller = "Home", + action = "Index", + id = UrlParameter.Optional + } + ); + } - protected void Application_Start() - { - // MVC setup documentation here: - // https://autofac.readthedocs.io/en/latest/integration/mvc.html - // WCF setup documentation here: - // https://autofac.readthedocs.io/en/latest/integration/wcf.html - // - // First we'll register the MVC/WCF stuff... - var builder = new ContainerBuilder(); + protected static void Application_Start() + { + // MVC setup documentation here: + // https://autofac.readthedocs.io/en/latest/integration/mvc.html + // WCF setup documentation here: + // https://autofac.readthedocs.io/en/latest/integration/wcf.html + // + // First we'll register the MVC/WCF stuff... + var builder = new ContainerBuilder(); - // MVC - Register your MVC controllers. - builder.RegisterControllers(typeof(MvcApplication).Assembly); + // MVC - Register your MVC controllers. + builder.RegisterControllers(typeof(MvcApplication).Assembly); - // MVC - OPTIONAL: Register model binders that require DI. - builder.RegisterModelBinders(typeof(MvcApplication).Assembly); - builder.RegisterModelBinderProvider(); + // MVC - OPTIONAL: Register model binders that require DI. + builder.RegisterModelBinders(typeof(MvcApplication).Assembly); + builder.RegisterModelBinderProvider(); - // MVC - OPTIONAL: Register web abstractions like HttpContextBase. - builder.RegisterModule(); + // MVC - OPTIONAL: Register web abstractions like HttpContextBase. + builder.RegisterModule(); - // MVC - OPTIONAL: Enable property injection in view pages. - builder.RegisterSource(new ViewRegistrationSource()); + // MVC - OPTIONAL: Enable property injection in view pages. + builder.RegisterSource(new ViewRegistrationSource()); - // MVC - OPTIONAL: Enable property injection into action filters. - builder.RegisterFilterProvider(); + // MVC - OPTIONAL: Enable property injection into action filters. + builder.RegisterFilterProvider(); - // WCF - Register channel factory and channel for service clients. - builder - .Register(c => new ChannelFactory("BasicHttpBinding_IService")) - .SingleInstance(); - builder - .Register(c => c.Resolve>().CreateChannel()) - .As() - .UseWcfSafeRelease(); + // WCF - Register channel factory and channel for service clients. + builder + .Register(c => new ChannelFactory("BasicHttpBinding_IService")) + .SingleInstance(); + builder + .Register(c => c.Resolve>().CreateChannel()) + .As() + .UseWcfSafeRelease(); - // Register application dependencies. - builder.RegisterType().As(); - builder.RegisterType().As(); + // Register application dependencies. + builder.RegisterType().As(); + builder.RegisterType().As(); - // MVC - Set the dependency resolver to be Autofac. - var container = builder.Build(); - DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); + // MVC - Set the dependency resolver to be Autofac. + var container = builder.Build(); + DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); - // Standard MVC setup: - AreaRegistration.RegisterAllAreas(); - RegisterGlobalFilters(GlobalFilters.Filters); - RegisterRoutes(RouteTable.Routes); - RegisterBundles(BundleTable.Bundles); - } + // Standard MVC setup: + AreaRegistration.RegisterAllAreas(); + RegisterGlobalFilters(GlobalFilters.Filters); + RegisterRoutes(RouteTable.Routes); + RegisterBundles(BundleTable.Bundles); } } diff --git a/src/MvcExample/Models/DependencyValueModel.cs b/src/MvcExample/Models/DependencyValueModel.cs index 1c6a71f..b26aade 100644 --- a/src/MvcExample/Models/DependencyValueModel.cs +++ b/src/MvcExample/Models/DependencyValueModel.cs @@ -1,32 +1,31 @@ using System; -namespace MvcExample.Models +namespace MvcExample.Models; + +/// +/// Model used to gather values from dependencies and display them. +/// +public class DependencyValueModel { /// - /// Model used to gather values from dependencies and display them. + /// Gets or sets the value retrieved from the action filter. /// - public class DependencyValueModel + /// + /// An with the current date and time as ticks. + /// + public long FilterValue { - /// - /// Gets or sets the value retrieved from the action filter. - /// - /// - /// An with the current date and time as ticks. - /// - public long FilterValue - { - get; set; - } + get; set; + } - /// - /// Gets or sets the ID of the WCF service dependency. - /// - /// - /// The that identifies the dependency injected into the WCF service. - /// - public Guid WcfServiceDependencyId - { - get; set; - } + /// + /// Gets or sets the ID of the WCF service dependency. + /// + /// + /// The that identifies the dependency injected into the WCF service. + /// + public Guid WcfServiceDependencyId + { + get; set; } } diff --git a/src/WcfExample/Dependencies/Dependency.cs b/src/WcfExample/Dependencies/Dependency.cs index 20d830c..4f8256e 100644 --- a/src/WcfExample/Dependencies/Dependency.cs +++ b/src/WcfExample/Dependencies/Dependency.cs @@ -1,30 +1,29 @@ using System; -namespace WcfExample.Dependencies +namespace WcfExample.Dependencies; + +/// +/// Simple dependency implementation. +/// +public class Dependency : IDependency { /// - /// Simple dependency implementation. + /// Initializes a new instance of the class. /// - public class Dependency : IDependency + public Dependency() { - /// - /// Initializes a new instance of the class. - /// - public Dependency() - { - InstanceId = Guid.NewGuid(); - } + InstanceId = Guid.NewGuid(); + } - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - public Guid InstanceId - { - get; private set; - } + /// + /// Gets the unique instance ID for the dependency. + /// + /// + /// A that indicates the unique ID for the + /// instance. + /// + public Guid InstanceId + { + get; private set; } } diff --git a/src/WcfExample/Dependencies/IDependency.cs b/src/WcfExample/Dependencies/IDependency.cs index fcf0bec..de59d65 100644 --- a/src/WcfExample/Dependencies/IDependency.cs +++ b/src/WcfExample/Dependencies/IDependency.cs @@ -1,23 +1,22 @@ using System; -namespace WcfExample.Dependencies +namespace WcfExample.Dependencies; + +/// +/// Demonstration dependency interface that allows you to inspect the unique +/// ID on a specific resolved instance of the dependency. +/// +public interface IDependency { /// - /// Demonstration dependency interface that allows you to inspect the unique - /// ID on a specific resolved instance of the dependency. + /// Gets the unique instance ID for the dependency. /// - public interface IDependency + /// + /// A that indicates the unique ID for the + /// instance. + /// + Guid InstanceId { - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - Guid InstanceId - { - get; - } + get; } } diff --git a/src/WcfExample/GetServiceInfoResponse.cs b/src/WcfExample/GetServiceInfoResponse.cs index 716b63c..fd0fb7b 100644 --- a/src/WcfExample/GetServiceInfoResponse.cs +++ b/src/WcfExample/GetServiceInfoResponse.cs @@ -1,36 +1,35 @@ using System; using System.ServiceModel; -namespace WcfExample +namespace WcfExample; + +/// +/// Response message contract for the service info operation. +/// +[MessageContract] +public class GetServiceInfoResponse { /// - /// Response message contract for the service info operation. + /// Gets or sets the service implementation type name. /// - [MessageContract] - public class GetServiceInfoResponse + /// + /// A with the service implementation type name to display. + /// + [MessageBodyMember] + public string? ServiceImplementationTypeName { - /// - /// Gets or sets the service implementation type name. - /// - /// - /// A with the service implementation type name to display. - /// - [MessageBodyMember] - public string ServiceImplementationTypeName - { - get; set; - } + get; set; + } - /// - /// Gets or sets the dependency instance ID. - /// - /// - /// A that indicates the unique ID for the dependency instance. - /// - [MessageBodyMember] - public Guid DependencyInstanceId - { - get; set; - } + /// + /// Gets or sets the dependency instance ID. + /// + /// + /// A that indicates the unique ID for the dependency instance. + /// + [MessageBodyMember] + public Guid DependencyInstanceId + { + get; set; } } diff --git a/src/WcfExample/Global.asax.cs b/src/WcfExample/Global.asax.cs index 9177409..4c0bb5d 100644 --- a/src/WcfExample/Global.asax.cs +++ b/src/WcfExample/Global.asax.cs @@ -4,27 +4,26 @@ using Autofac.Integration.Wcf; using WcfExample.Dependencies; -namespace WcfExample +namespace WcfExample; + +public class Global : HttpApplication { - public class Global : HttpApplication + protected void Application_Start(object sender, EventArgs e) { - protected void Application_Start(object sender, EventArgs e) - { - // WCF integration docs are at: - // https://autofac.readthedocs.io/en/latest/integration/wcf.html - var builder = new ContainerBuilder(); + // WCF integration docs are at: + // https://autofac.readthedocs.io/en/latest/integration/wcf.html + var builder = new ContainerBuilder(); - // Register your service implementations. - builder.RegisterType(); - builder.RegisterType(); + // Register your service implementations. + builder.RegisterType(); + builder.RegisterType(); - // Register your dependencies. - builder.RegisterType().As(); + // Register your dependencies. + builder.RegisterType().As(); - // Set the dependency resolver. This works for both regular - // WCF services and REST-enabled services. - var container = builder.Build(); - AutofacHostFactory.Container = container; - } + // Set the dependency resolver. This works for both regular + // WCF services and REST-enabled services. + var container = builder.Build(); + AutofacHostFactory.Container = container; } } diff --git a/src/WcfExample/HostFactoryService.svc.cs b/src/WcfExample/HostFactoryService.svc.cs index eac1a24..06e9ff9 100644 --- a/src/WcfExample/HostFactoryService.svc.cs +++ b/src/WcfExample/HostFactoryService.svc.cs @@ -1,26 +1,25 @@ using WcfExample.Dependencies; -namespace WcfExample +namespace WcfExample; + +public class HostFactoryService : IService { - public class HostFactoryService : IService + public HostFactoryService(IDependency dependency) { - public HostFactoryService(IDependency dependency) - { - Dependency = dependency; - } + Dependency = dependency; + } - public IDependency Dependency - { - get; private set; - } + public IDependency Dependency + { + get; private set; + } - public GetServiceInfoResponse GetServiceInfo() + public GetServiceInfoResponse GetServiceInfo() + { + return new GetServiceInfoResponse { - return new GetServiceInfoResponse - { - DependencyInstanceId = Dependency.InstanceId, - ServiceImplementationTypeName = GetType().FullName - }; - } + DependencyInstanceId = Dependency.InstanceId, + ServiceImplementationTypeName = GetType().FullName + }; } } diff --git a/src/WcfExample/IService.cs b/src/WcfExample/IService.cs index 3e2e3ad..e4f97e1 100644 --- a/src/WcfExample/IService.cs +++ b/src/WcfExample/IService.cs @@ -1,13 +1,12 @@ using System.ServiceModel; using System.ServiceModel.Web; -namespace WcfExample +namespace WcfExample; + +[ServiceContract] +public interface IService { - [ServiceContract] - public interface IService - { - [OperationContract] - [WebGet(UriTemplate = "GetInfo")] - GetServiceInfoResponse GetServiceInfo(); - } + [OperationContract] + [WebGet(UriTemplate = "GetInfo")] + GetServiceInfoResponse GetServiceInfo(); } diff --git a/src/WcfExample/WebHostFactoryService.svc.cs b/src/WcfExample/WebHostFactoryService.svc.cs index 5a8130c..79b29a2 100644 --- a/src/WcfExample/WebHostFactoryService.svc.cs +++ b/src/WcfExample/WebHostFactoryService.svc.cs @@ -1,32 +1,31 @@ using WcfExample.Dependencies; -namespace WcfExample +namespace WcfExample; + +/// +/// REST-enabled version of the service. Call +/// http://localhost:25665/WebHostFactoryService.svc/GetInfo +/// to see this execute. +/// +/// +public class WebHostFactoryService : IService { - /// - /// REST-enabled version of the service. Call - /// http://localhost:25665/WebHostFactoryService.svc/GetInfo - /// to see this execute. - /// - /// - public class WebHostFactoryService : IService + public WebHostFactoryService(IDependency dependency) { - public WebHostFactoryService(IDependency dependency) - { - Dependency = dependency; - } + Dependency = dependency; + } - public IDependency Dependency - { - get; private set; - } + public IDependency Dependency + { + get; private set; + } - public GetServiceInfoResponse GetServiceInfo() + public GetServiceInfoResponse GetServiceInfo() + { + return new GetServiceInfoResponse { - return new GetServiceInfoResponse - { - DependencyInstanceId = Dependency.InstanceId, - ServiceImplementationTypeName = GetType().FullName - }; - } + DependencyInstanceId = Dependency.InstanceId, + ServiceImplementationTypeName = GetType().FullName + }; } } diff --git a/src/WebApiExample.OwinSelfHost/CustomActionFilter.cs b/src/WebApiExample.OwinSelfHost/CustomActionFilter.cs index 1c5afde..88c47ff 100644 --- a/src/WebApiExample.OwinSelfHost/CustomActionFilter.cs +++ b/src/WebApiExample.OwinSelfHost/CustomActionFilter.cs @@ -4,27 +4,26 @@ using System.Web.Http.Filters; using Autofac.Integration.WebApi; -namespace WebApiExample.OwinSelfHost +namespace WebApiExample.OwinSelfHost; + +public class CustomActionFilter : IAutofacActionFilter { - public class CustomActionFilter : IAutofacActionFilter - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public CustomActionFilter(ILogger logger) - { - _logger = logger; - } + public CustomActionFilter(ILogger logger) + { + _logger = logger; + } - public Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) - { - _logger.Write("Inside the 'OnActionExecutedAsync' method of the custom action filter."); - return Task.FromResult(0); - } + public Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) + { + _logger.Write("Inside the 'OnActionExecutedAsync' method of the custom action filter."); + return Task.FromResult(0); + } - public Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken) - { - _logger.Write("Inside the 'OnActionExecutingAsync' method of the custom action filter."); - return Task.FromResult(0); - } + public Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken) + { + _logger.Write("Inside the 'OnActionExecutingAsync' method of the custom action filter."); + return Task.FromResult(0); } } diff --git a/src/WebApiExample.OwinSelfHost/FirstMiddleware.cs b/src/WebApiExample.OwinSelfHost/FirstMiddleware.cs index 8613892..d9a066a 100644 --- a/src/WebApiExample.OwinSelfHost/FirstMiddleware.cs +++ b/src/WebApiExample.OwinSelfHost/FirstMiddleware.cs @@ -1,22 +1,21 @@ using System.Threading.Tasks; using Microsoft.Owin; -namespace WebApiExample.OwinSelfHost +namespace WebApiExample.OwinSelfHost; + +public class FirstMiddleware : OwinMiddleware { - public class FirstMiddleware : OwinMiddleware - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public FirstMiddleware(OwinMiddleware next, ILogger logger) : base(next) - { - _logger = logger; - } + public FirstMiddleware(OwinMiddleware next, ILogger logger) : base(next) + { + _logger = logger; + } - public override async Task Invoke(IOwinContext context) - { - _logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name); + public override async Task Invoke(IOwinContext context) + { + _logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name); - await Next.Invoke(context); - } + await Next.Invoke(context); } } diff --git a/src/WebApiExample.OwinSelfHost/ILogger.cs b/src/WebApiExample.OwinSelfHost/ILogger.cs index 76a227b..0695746 100644 --- a/src/WebApiExample.OwinSelfHost/ILogger.cs +++ b/src/WebApiExample.OwinSelfHost/ILogger.cs @@ -1,7 +1,6 @@ -namespace WebApiExample.OwinSelfHost +namespace WebApiExample.OwinSelfHost; + +public interface ILogger { - public interface ILogger - { - void Write(string message, params object[] args); - } + void Write(string message, params object[] args); } diff --git a/src/WebApiExample.OwinSelfHost/Logger.cs b/src/WebApiExample.OwinSelfHost/Logger.cs index 96f28a8..4b8eea1 100644 --- a/src/WebApiExample.OwinSelfHost/Logger.cs +++ b/src/WebApiExample.OwinSelfHost/Logger.cs @@ -1,12 +1,11 @@ using System.Diagnostics; -namespace WebApiExample.OwinSelfHost +namespace WebApiExample.OwinSelfHost; + +public class Logger : ILogger { - public class Logger : ILogger + public void Write(string message, params object[] args) { - public void Write(string message, params object[] args) - { - Debug.WriteLine(message, args); - } + Debug.WriteLine(message, args); } } diff --git a/src/WebApiExample.OwinSelfHost/Program.cs b/src/WebApiExample.OwinSelfHost/Program.cs index e84ce20..408026e 100644 --- a/src/WebApiExample.OwinSelfHost/Program.cs +++ b/src/WebApiExample.OwinSelfHost/Program.cs @@ -3,34 +3,33 @@ using System.Net.Http; using Microsoft.Owin.Hosting; -namespace WebApiExample.OwinSelfHost +namespace WebApiExample.OwinSelfHost; + +internal class Program { - internal class Program + private static void Main() { - private static void Main() - { - const string BaseAddress = "http://localhost:9123/"; + const string BaseAddress = "http://localhost:9123/"; - // This starts the OWIN host using the application startup - // logic in the Startup class. See Startup for the example of - // how to set up OWIN Web API. - using (WebApp.Start(BaseAddress)) - { - // On startup this app will make a request to the self-hosted - // Web API service. You should see logging statements and results - // dumped to the console window. - var client = new HttpClient(); - var response = client.GetAsync(BaseAddress + "api/test").Result; + // This starts the OWIN host using the application startup + // logic in the Startup class. See Startup for the example of + // how to set up OWIN Web API. + using (WebApp.Start(BaseAddress)) + { + // On startup this app will make a request to the self-hosted + // Web API service. You should see logging statements and results + // dumped to the console window. + var client = new HttpClient(); + var response = client.GetAsync(BaseAddress + "api/test").Result; - Console.WriteLine(response); - Console.WriteLine(response.Content.ReadAsStringAsync().Result); - } + Console.WriteLine(response); + Console.WriteLine(response.Content.ReadAsStringAsync().Result); + } - if (Debugger.IsAttached) - { - Console.WriteLine("Press any key to exit."); - Console.ReadLine(); - } + if (Debugger.IsAttached) + { + Console.WriteLine("Press any key to exit."); + Console.ReadLine(); } } } diff --git a/src/WebApiExample.OwinSelfHost/SecondMiddleware.cs b/src/WebApiExample.OwinSelfHost/SecondMiddleware.cs index df71ad3..56a1ace 100644 --- a/src/WebApiExample.OwinSelfHost/SecondMiddleware.cs +++ b/src/WebApiExample.OwinSelfHost/SecondMiddleware.cs @@ -1,22 +1,21 @@ using System.Threading.Tasks; using Microsoft.Owin; -namespace WebApiExample.OwinSelfHost +namespace WebApiExample.OwinSelfHost; + +public class SecondMiddleware : OwinMiddleware { - public class SecondMiddleware : OwinMiddleware - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public SecondMiddleware(OwinMiddleware next, ILogger logger) : base(next) - { - _logger = logger; - } + public SecondMiddleware(OwinMiddleware next, ILogger logger) : base(next) + { + _logger = logger; + } - public override async Task Invoke(IOwinContext context) - { - _logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name); + public override async Task Invoke(IOwinContext context) + { + _logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name); - await Next.Invoke(context); - } + await Next.Invoke(context); } } diff --git a/src/WebApiExample.OwinSelfHost/Startup.cs b/src/WebApiExample.OwinSelfHost/Startup.cs index 5c6738b..20b91e4 100644 --- a/src/WebApiExample.OwinSelfHost/Startup.cs +++ b/src/WebApiExample.OwinSelfHost/Startup.cs @@ -4,67 +4,66 @@ using Autofac.Integration.WebApi; using Owin; -namespace WebApiExample.OwinSelfHost +namespace WebApiExample.OwinSelfHost; + +public class Startup { - public class Startup + public static void Configuration(IAppBuilder app) { - public void Configuration(IAppBuilder app) - { - // In OWIN you create your own HttpConfiguration rather than - // re-using the GlobalConfiguration. - var config = new HttpConfiguration(); + // In OWIN you create your own HttpConfiguration rather than + // re-using the GlobalConfiguration. + var config = new HttpConfiguration(); - config.Routes.MapHttpRoute( - "DefaultApi", - "api/{controller}/{id}", - new - { - id = RouteParameter.Optional - }); + config.Routes.MapHttpRoute( + "DefaultApi", + "api/{controller}/{id}", + new + { + id = RouteParameter.Optional + }); - var builder = new ContainerBuilder(); + var builder = new ContainerBuilder(); - // Register Web API controller in executing assembly. - builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); + // Register Web API controller in executing assembly. + builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); - // OPTIONAL - Register the filter provider if you have custom filters that need DI. - // Also hook the filters up to controllers. - builder.RegisterWebApiFilterProvider(config); - builder.RegisterType() - .AsWebApiActionFilterFor() - .InstancePerRequest(); + // OPTIONAL - Register the filter provider if you have custom filters that need DI. + // Also hook the filters up to controllers. + builder.RegisterWebApiFilterProvider(config); + builder.RegisterType() + .AsWebApiActionFilterFor() + .InstancePerRequest(); - // Register a logger service to be used by the controller and middleware. - builder.Register(c => new Logger()).As().InstancePerRequest(); + // Register a logger service to be used by the controller and middleware. + builder.Register(c => new Logger()).As().InstancePerRequest(); - // Autofac will add middleware to IAppBuilder in the order registered. - // The middleware will execute in the order added to IAppBuilder. - builder.RegisterType().InstancePerRequest(); - builder.RegisterType().InstancePerRequest(); + // Autofac will add middleware to IAppBuilder in the order registered. + // The middleware will execute in the order added to IAppBuilder. + builder.RegisterType().InstancePerRequest(); + builder.RegisterType().InstancePerRequest(); - // Create and assign a dependency resolver for Web API to use. - var container = builder.Build(); - config.DependencyResolver = new AutofacWebApiDependencyResolver(container); + // Create and assign a dependency resolver for Web API to use. + var container = builder.Build(); + config.DependencyResolver = new AutofacWebApiDependencyResolver(container); - // The Autofac middleware should be the first middleware added to the IAppBuilder. - // If you "UseAutofacMiddleware" then all of the middleware in the container - // will be injected into the pipeline right after the Autofac lifetime scope - // is created/injected. - // - // Alternatively, you can control when container-based - // middleware is used by using "UseAutofacLifetimeScopeInjector" along with - // "UseMiddlewareFromContainer". As long as the lifetime scope injector - // comes first, everything is good. - app.UseAutofacMiddleware(container); + // The Autofac middleware should be the first middleware added to the IAppBuilder. + // If you "UseAutofacMiddleware" then all of the middleware in the container + // will be injected into the pipeline right after the Autofac lifetime scope + // is created/injected. + // + // Alternatively, you can control when container-based + // middleware is used by using "UseAutofacLifetimeScopeInjector" along with + // "UseMiddlewareFromContainer". As long as the lifetime scope injector + // comes first, everything is good. + app.UseAutofacMiddleware(container); - // Again, the alternative to "UseAutofacMiddleware" is something like this: - // app.UseAutofacLifetimeScopeInjector(container); - // app.UseMiddlewareFromContainer(); - // app.UseMiddlewareFromContainer(); + // Again, the alternative to "UseAutofacMiddleware" is something like this: + // app.UseAutofacLifetimeScopeInjector(container); + // app.UseMiddlewareFromContainer(); + // app.UseMiddlewareFromContainer(); - // Make sure the Autofac lifetime scope is passed to Web API. - app.UseAutofacWebApi(config); - app.UseWebApi(config); - } + // Make sure the Autofac lifetime scope is passed to Web API. + app.UseAutofacWebApi(config); + app.UseWebApi(config); } } diff --git a/src/WebApiExample.OwinSelfHost/TestController.cs b/src/WebApiExample.OwinSelfHost/TestController.cs index 3eb616c..bd4acbd 100644 --- a/src/WebApiExample.OwinSelfHost/TestController.cs +++ b/src/WebApiExample.OwinSelfHost/TestController.cs @@ -1,21 +1,20 @@ using System.Web.Http; -namespace WebApiExample.OwinSelfHost +namespace WebApiExample.OwinSelfHost; + +public class TestController : ApiController { - public class TestController : ApiController - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public TestController(ILogger logger) - { - _logger = logger; - } + public TestController(ILogger logger) + { + _logger = logger; + } - public string Get() - { - _logger.Write("Inside the 'Get' method of the '{0}' controller.", GetType().Name); + public string Get() + { + _logger.Write("Inside the 'Get' method of the '{0}' controller.", GetType().Name); - return "Hello, world!"; - } + return "Hello, world!"; } } diff --git a/src/WebFormsExample/About.aspx.cs b/src/WebFormsExample/About.aspx.cs index 9123ce4..4e1ab7f 100644 --- a/src/WebFormsExample/About.aspx.cs +++ b/src/WebFormsExample/About.aspx.cs @@ -1,8 +1,7 @@ using System.Web.UI; -namespace WebFormsExample +namespace WebFormsExample; + +public partial class About : Page { - public partial class About : Page - { - } } diff --git a/src/WebFormsExample/Default.aspx.cs b/src/WebFormsExample/Default.aspx.cs index d02d7b1..a22edff 100644 --- a/src/WebFormsExample/Default.aspx.cs +++ b/src/WebFormsExample/Default.aspx.cs @@ -3,21 +3,20 @@ using System.Web.UI; using WebFormsExample.Dependencies; -namespace WebFormsExample +namespace WebFormsExample; + +[SuppressMessage("IDE1006", "IDE1006", Justification = "Underscore is required to avoid conflict with reserved keyword 'default'.")] +public partial class _Default : Page { - [SuppressMessage("IDE1006", "IDE1006", Justification = "Underscore is required to avoid conflict with reserved keyword 'default'.")] - public partial class _Default : Page + // This property will be set for you by the PropertyInjectionModule. + public IDependency? Dependency { - // This property will be set for you by the PropertyInjectionModule. - public IDependency Dependency - { - get; set; - } + get; set; + } - protected void Page_Load(object sender, EventArgs e) - { - // Now you can use the property that was set for you. - DependencyLabel.Text = Dependency.InstanceId.ToString(); - } + protected void Page_Load(object sender, EventArgs e) + { + // Now you can use the property that was set for you. + DependencyLabel.Text = Dependency.InstanceId.ToString(); } } diff --git a/src/WebFormsExample/Dependencies/Dependency.cs b/src/WebFormsExample/Dependencies/Dependency.cs index 9124dd5..54927b1 100644 --- a/src/WebFormsExample/Dependencies/Dependency.cs +++ b/src/WebFormsExample/Dependencies/Dependency.cs @@ -1,30 +1,29 @@ using System; -namespace WebFormsExample.Dependencies +namespace WebFormsExample.Dependencies; + +/// +/// Simple dependency implementation. +/// +public class Dependency : IDependency { /// - /// Simple dependency implementation. + /// Initializes a new instance of the class. /// - public class Dependency : IDependency + public Dependency() { - /// - /// Initializes a new instance of the class. - /// - public Dependency() - { - InstanceId = Guid.NewGuid(); - } + InstanceId = Guid.NewGuid(); + } - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - public Guid InstanceId - { - get; private set; - } + /// + /// Gets the unique instance ID for the dependency. + /// + /// + /// A that indicates the unique ID for the + /// instance. + /// + public Guid InstanceId + { + get; private set; } } diff --git a/src/WebFormsExample/Dependencies/IDependency.cs b/src/WebFormsExample/Dependencies/IDependency.cs index 89c2d23..433f6ac 100644 --- a/src/WebFormsExample/Dependencies/IDependency.cs +++ b/src/WebFormsExample/Dependencies/IDependency.cs @@ -1,23 +1,22 @@ using System; -namespace WebFormsExample.Dependencies +namespace WebFormsExample.Dependencies; + +/// +/// Demonstration dependency interface that allows you to inspect the unique +/// ID on a specific resolved instance of the dependency. +/// +public interface IDependency { /// - /// Demonstration dependency interface that allows you to inspect the unique - /// ID on a specific resolved instance of the dependency. + /// Gets the unique instance ID for the dependency. /// - public interface IDependency + /// + /// A that indicates the unique ID for the + /// instance. + /// + Guid InstanceId { - /// - /// Gets the unique instance ID for the dependency. - /// - /// - /// A that indicates the unique ID for the - /// instance. - /// - Guid InstanceId - { - get; - } + get; } } diff --git a/src/WebFormsExample/Global.asax.cs b/src/WebFormsExample/Global.asax.cs index 4e64cd1..36891e1 100644 --- a/src/WebFormsExample/Global.asax.cs +++ b/src/WebFormsExample/Global.asax.cs @@ -6,44 +6,43 @@ using Microsoft.AspNet.FriendlyUrls; using WebFormsExample.Dependencies; -namespace WebFormsExample +namespace WebFormsExample; + +public class Global : HttpApplication, IContainerProviderAccessor { - public class Global : HttpApplication, IContainerProviderAccessor - { - // Provider that holds the application container. - private static IContainerProvider _containerProvider; + // Provider that holds the application container. + private static IContainerProvider? _containerProvider; - // Instance property that will be used by Autofac HttpModules - // to resolve and inject dependencies. - public IContainerProvider ContainerProvider + // Instance property that will be used by Autofac HttpModules + // to resolve and inject dependencies. + public IContainerProvider ContainerProvider + { + get { - get - { - return _containerProvider; - } + return _containerProvider; } + } - public static void RegisterRoutes(RouteCollection routes) + public static void RegisterRoutes(RouteCollection routes) + { + var settings = new FriendlyUrlSettings { - var settings = new FriendlyUrlSettings - { - AutoRedirectMode = RedirectMode.Permanent - }; - routes.EnableFriendlyUrls(settings); - } + AutoRedirectMode = RedirectMode.Permanent + }; + routes.EnableFriendlyUrls(settings); + } - private void Application_Start(object sender, EventArgs e) - { - // Build up your application container and register your dependencies. - var builder = new ContainerBuilder(); - builder.RegisterType().As(); + private void Application_Start(object sender, EventArgs e) + { + // Build up your application container and register your dependencies. + var builder = new ContainerBuilder(); + builder.RegisterType().As(); - // Once you're done registering things, set the container - // provider up with your registrations. - _containerProvider = new ContainerProvider(builder.Build()); + // Once you're done registering things, set the container + // provider up with your registrations. + _containerProvider = new ContainerProvider(builder.Build()); - // Standard web forms startup. - RegisterRoutes(RouteTable.Routes); - } + // Standard web forms startup. + RegisterRoutes(RouteTable.Routes); } } diff --git a/src/WebFormsExample/Site.Master.cs b/src/WebFormsExample/Site.Master.cs index 3fb4733..51bf62a 100644 --- a/src/WebFormsExample/Site.Master.cs +++ b/src/WebFormsExample/Site.Master.cs @@ -1,13 +1,12 @@ using System; using System.Web.UI; -namespace WebFormsExample +namespace WebFormsExample; + +public partial class SiteMaster : MasterPage { - public partial class SiteMaster : MasterPage + protected void Page_Load(object sender, EventArgs e) { - protected void Page_Load(object sender, EventArgs e) - { - } } } From 6df2893087b8e84c863d85cabadf377112a8e0d0 Mon Sep 17 00:00:00 2001 From: Travis Illig Date: Tue, 1 Sep 2026 08:03:58 -0700 Subject: [PATCH 3/6] Ignore the file-scoped namespace conversion in git blame Part of #32 --- .git-blame-ignore-revs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 3722308..37bd7fd 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -15,3 +15,6 @@ ccd9b352d4066839cbe2dff010df6ab464ea871e # Apply dotnet format across the solution. 2fea688003a3947a656d91e0532cd2dff2a737c6 + +# Convert to file-scoped namespaces and drop redundant usings. +f089e7856c50b437f6583ba069a3ed553d1f7d61 From 9595db9b491f93f3633017983362225383a98fde Mon Sep 17 00:00:00 2001 From: Travis Illig Date: Tue, 1 Sep 2026 08:04:36 -0700 Subject: [PATCH 4/6] Fix the nullable and analyzer warnings the new rules surfaced Mostly narrow: null-conditional where a value really can be absent, sealing internal types with no subtypes, and PascalCase logging placeholders. The null-forgiving operators in the Web Forms page, the MVC action filter, and the Global.asax container provider all sit on values Autofac property injection or application startup guarantees. Two are more than cosmetic. The multitenant console app held its container and tenant strategy in nullable statics assigned from Main, which meant every use needed a null check the design never actually allowed; they are now readonly fields initialized where they're declared, with the strategy passed in rather than read back off a field. And the MVC actions get [HttpGet], because without it they also accept POST, which is what CA3147 was objecting to. Also drops the last ReSharper suppression comment. Part of #32 --- .../Services/ValuesService.cs | 4 ++-- src/AttributeMetadataExample/Log.cs | 2 +- src/ConfigurationExample/Program.cs | 2 +- src/GenericHostBuilderExample/HostedService.cs | 3 +-- src/GenericHostBuilderExample/Logger.cs | 2 +- .../ManualTenantIdentificationStrategy.cs | 2 +- .../Program.cs | 18 ++++++------------ .../Controllers/HomeController.cs | 2 ++ src/MvcExample/Controllers/HomeController.cs | 2 ++ src/MvcExample/CustomActionFilterAttribute.cs | 2 +- src/WebApiExample.OwinSelfHost/Program.cs | 2 +- src/WebFormsExample/Default.aspx.cs | 2 +- src/WebFormsExample/Global.asax.cs | 2 +- 13 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/AspNetCoreExample/Services/ValuesService.cs b/src/AspNetCoreExample/Services/ValuesService.cs index 0436760..6b250b6 100644 --- a/src/AspNetCoreExample/Services/ValuesService.cs +++ b/src/AspNetCoreExample/Services/ValuesService.cs @@ -11,14 +11,14 @@ public ValuesService(ILogger logger) public IEnumerable FindAll() { - _logger.LogDebug("{method} called", nameof(FindAll)); + _logger.LogDebug("{Method} called", nameof(FindAll)); return new[] { "value1", "value2" }; } public string Find(int id) { - _logger.LogDebug("{method} called with {id}", nameof(Find), id); + _logger.LogDebug("{Method} called with {Id}", nameof(Find), id); return $"value{id}"; } diff --git a/src/AttributeMetadataExample/Log.cs b/src/AttributeMetadataExample/Log.cs index 85e5418..52c0c31 100644 --- a/src/AttributeMetadataExample/Log.cs +++ b/src/AttributeMetadataExample/Log.cs @@ -13,7 +13,7 @@ public Log(IEnumerable> appenders) public void Write(string destination, string message) { - var appender = _appenders.First(a => a.Metadata["AppenderName"].Equals(destination)); + var appender = _appenders.First(a => destination.Equals(a.Metadata["AppenderName"])); appender.Value.Write(message); } } diff --git a/src/ConfigurationExample/Program.cs b/src/ConfigurationExample/Program.cs index f9a2f54..4bbbccd 100644 --- a/src/ConfigurationExample/Program.cs +++ b/src/ConfigurationExample/Program.cs @@ -32,7 +32,7 @@ public static void Main() // https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/corehost.md // // To verify, try commenting this out and you'll see that the config system can't load the external plugin type. - var executionFolder = Path.GetDirectoryName(typeof(Program).Assembly.Location); + var executionFolder = Path.GetDirectoryName(typeof(Program).Assembly.Location) ?? AppContext.BaseDirectory; AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext context, AssemblyName assembly) => context.LoadFromAssemblyPath(Path.Combine(executionFolder, $"{assembly.Name}.dll")); var config = new ConfigurationBuilder() diff --git a/src/GenericHostBuilderExample/HostedService.cs b/src/GenericHostBuilderExample/HostedService.cs index 27d31e8..37fc84e 100644 --- a/src/GenericHostBuilderExample/HostedService.cs +++ b/src/GenericHostBuilderExample/HostedService.cs @@ -2,11 +2,10 @@ namespace GenericHostBuilderExample; -internal class HostedService : IHostedService +internal sealed class HostedService : IHostedService { private readonly ILogger _logger; - // ReSharper disable once UnusedMember.Global public HostedService(ILogger logger) { _logger = logger; diff --git a/src/GenericHostBuilderExample/Logger.cs b/src/GenericHostBuilderExample/Logger.cs index 95dc317..de0ee4b 100644 --- a/src/GenericHostBuilderExample/Logger.cs +++ b/src/GenericHostBuilderExample/Logger.cs @@ -1,6 +1,6 @@ namespace GenericHostBuilderExample; -internal class Logger : ILogger +internal sealed class Logger : ILogger { public async Task Log(string value) { diff --git a/src/MultitenantExample.ConsoleApplication/ManualTenantIdentificationStrategy.cs b/src/MultitenantExample.ConsoleApplication/ManualTenantIdentificationStrategy.cs index a17b669..14a9fa7 100644 --- a/src/MultitenantExample.ConsoleApplication/ManualTenantIdentificationStrategy.cs +++ b/src/MultitenantExample.ConsoleApplication/ManualTenantIdentificationStrategy.cs @@ -40,7 +40,7 @@ public object? CurrentTenantId /// public bool TryIdentifyTenant(out object? tenantId) { - if (CurrentTenantId.ToString() == "0") + if (CurrentTenantId?.ToString() == "0") { // 0 is the "default tenant ID" tenantId = null; diff --git a/src/MultitenantExample.ConsoleApplication/Program.cs b/src/MultitenantExample.ConsoleApplication/Program.cs index 3574ba8..f735316 100644 --- a/src/MultitenantExample.ConsoleApplication/Program.cs +++ b/src/MultitenantExample.ConsoleApplication/Program.cs @@ -17,26 +17,20 @@ namespace MultitenantExample.ConsoleApplication; public class Program { /// - /// The container from which dependencies will be resolved. + /// Strategy used for identifying the current tenant with multitenant DI. /// - private static IContainer? _container; + private static readonly ManualTenantIdentificationStrategy _tenantIdentifier = new(); /// - /// Strategy used for identifying the current tenant with multitenant DI. + /// The container from which dependencies will be resolved. /// - private static ManualTenantIdentificationStrategy? _tenantIdentifier; + private static readonly MultitenantContainer _container = ConfigureDependencies(_tenantIdentifier); /// /// Demo program entry point. /// public static void Main() { - // Initialize the tenant identification strategy. - _tenantIdentifier = new ManualTenantIdentificationStrategy(); - - // Set the application container to the multitenant container. - _container = ConfigureDependencies(); - // Explain what you're looking at. WriteInstructions(); @@ -47,7 +41,7 @@ public static void Main() /// /// Configures the multitenant dependency container. /// - private static IContainer ConfigureDependencies() + private static MultitenantContainer ConfigureDependencies(ManualTenantIdentificationStrategy tenantIdentifier) { // Register default dependencies in the application container. var builder = new ContainerBuilder(); @@ -56,7 +50,7 @@ private static IContainer ConfigureDependencies() var appContainer = builder.Build(); // Create the multitenant container. - var mtc = new MultitenantContainer(_tenantIdentifier, appContainer); + var mtc = new MultitenantContainer(tenantIdentifier, appContainer); // Configure overrides for tenant 1. Tenant 1 registers their dependencies // as instance-per-dependency. diff --git a/src/MultitenantExample.MvcApplication/Controllers/HomeController.cs b/src/MultitenantExample.MvcApplication/Controllers/HomeController.cs index b476133..2d11d75 100644 --- a/src/MultitenantExample.MvcApplication/Controllers/HomeController.cs +++ b/src/MultitenantExample.MvcApplication/Controllers/HomeController.cs @@ -38,11 +38,13 @@ public ITenantIdentificationStrategy TenantIdentificationStrategy get; set; } + [HttpGet] public ActionResult About() { return View(); } + [HttpGet] public virtual ActionResult Index() { var model = BuildIndexModel(); diff --git a/src/MvcExample/Controllers/HomeController.cs b/src/MvcExample/Controllers/HomeController.cs index 863b325..0a0c435 100644 --- a/src/MvcExample/Controllers/HomeController.cs +++ b/src/MvcExample/Controllers/HomeController.cs @@ -17,11 +17,13 @@ public IService ServiceClient get; private set; } + [HttpGet] public ActionResult About() { return View(); } + [HttpGet] [CustomActionFilter] public async Task Index() { diff --git a/src/MvcExample/CustomActionFilterAttribute.cs b/src/MvcExample/CustomActionFilterAttribute.cs index 63ccba7..28428e5 100644 --- a/src/MvcExample/CustomActionFilterAttribute.cs +++ b/src/MvcExample/CustomActionFilterAttribute.cs @@ -14,6 +14,6 @@ public override void OnActionExecuting(ActionExecutingContext filterContext) { // This filter adds a value from the dependency to the current context // so the controller can grab it and pass it to the view. - filterContext.HttpContext.Items["filterValue"] = Dependency.CurrentTicks; + filterContext.HttpContext.Items["filterValue"] = Dependency!.CurrentTicks; } } diff --git a/src/WebApiExample.OwinSelfHost/Program.cs b/src/WebApiExample.OwinSelfHost/Program.cs index 408026e..9d9c7aa 100644 --- a/src/WebApiExample.OwinSelfHost/Program.cs +++ b/src/WebApiExample.OwinSelfHost/Program.cs @@ -5,7 +5,7 @@ namespace WebApiExample.OwinSelfHost; -internal class Program +internal sealed class Program { private static void Main() { diff --git a/src/WebFormsExample/Default.aspx.cs b/src/WebFormsExample/Default.aspx.cs index a22edff..b1757ff 100644 --- a/src/WebFormsExample/Default.aspx.cs +++ b/src/WebFormsExample/Default.aspx.cs @@ -17,6 +17,6 @@ public IDependency? Dependency protected void Page_Load(object sender, EventArgs e) { // Now you can use the property that was set for you. - DependencyLabel.Text = Dependency.InstanceId.ToString(); + DependencyLabel.Text = Dependency!.InstanceId.ToString(); } } diff --git a/src/WebFormsExample/Global.asax.cs b/src/WebFormsExample/Global.asax.cs index 36891e1..1ba3d62 100644 --- a/src/WebFormsExample/Global.asax.cs +++ b/src/WebFormsExample/Global.asax.cs @@ -19,7 +19,7 @@ public IContainerProvider ContainerProvider { get { - return _containerProvider; + return _containerProvider!; } } From b1626bc244ad1702b27de48546209e8afc429570 Mon Sep 17 00:00:00 2001 From: Travis Illig Date: Tue, 1 Sep 2026 08:04:37 -0700 Subject: [PATCH 5/6] Give WebFormsExample its own assembly name Both RootNamespace and AssemblyName said MvcExample, so the Web Forms sample built an assembly named after an unrelated project while every type in it, and every Inherits attribute pointing at those types, used WebFormsExample. Part of #32 --- src/WebFormsExample/WebFormsExample.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WebFormsExample/WebFormsExample.csproj b/src/WebFormsExample/WebFormsExample.csproj index 78eefe1..9f65ffc 100644 --- a/src/WebFormsExample/WebFormsExample.csproj +++ b/src/WebFormsExample/WebFormsExample.csproj @@ -4,8 +4,8 @@ Library bin/ false - MvcExample - MvcExample + WebFormsExample + WebFormsExample true From 607c2cca7a047c5e527005061ef427a295ef8eba Mon Sep 17 00:00:00 2001 From: Travis Illig Date: Tue, 1 Sep 2026 08:31:59 -0700 Subject: [PATCH 6/6] Govern analyzer rules with rulesets instead of NoWarn Matches the Source.ruleset pattern the library repos use. The previous NoWarn was also far too broad: it suppressed five rules across all fifteen projects when only five projects needed anything, so a real CA1716 or CA5368 in a console example would have gone unreported. Splitting ASP.NET out of the common set is what the measurements asked for. CA1707, CA1716 and CA5368 only fire where System.Web dictates a type or handler name, and CA1848 and CA1873 only where ASP.NET Core logging is in play. None of them can occur in a plain console sample. Source.ruleset therefore carries no deviations yet and exists as the baseline every project inherits; AspNet.ruleset includes it and adds the eight hosted projects' exemptions. Also drops the NU1900 exclusion. That warning only appears against a feed without NuGetAudit support, so suppressing it locally is a local concern rather than something the public build should carry. Part of #32 --- Directory.Build.props | 15 +-------------- build/AspNet.ruleset | 17 +++++++++++++++++ build/Source.ruleset | 7 +++++++ .../AspNetCoreChildLifetimeScope.csproj | 1 + src/AspNetCoreExample/AspNetCoreExample.csproj | 1 + .../AspNetCoreNoStartupExample.csproj | 1 + .../MultitenantExample.MvcApplication.csproj | 1 + .../MultitenantExample.WcfService.csproj | 1 + src/MvcExample/MvcExample.csproj | 1 + src/WcfExample/WcfExample.csproj | 1 + src/WebFormsExample/WebFormsExample.csproj | 1 + 11 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 build/AspNet.ruleset create mode 100644 build/Source.ruleset diff --git a/Directory.Build.props b/Directory.Build.props index 39f92d6..5d022ce 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,23 +5,10 @@ enable latest-recommended true + $(MSBuildThisFileDirectory)build/Source.ruleset true - - NU1900 - - - - - $(NoWarn);CA1707;CA1716;CA1848;CA1873;CA5368 diff --git a/build/AspNet.ruleset b/build/AspNet.ruleset new file mode 100644 index 0000000..616489e --- /dev/null +++ b/build/AspNet.ruleset @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/build/Source.ruleset b/build/Source.ruleset new file mode 100644 index 0000000..5e7bde7 --- /dev/null +++ b/build/Source.ruleset @@ -0,0 +1,7 @@ + + + + diff --git a/src/AspNetCoreChildLifetimeScope/AspNetCoreChildLifetimeScope.csproj b/src/AspNetCoreChildLifetimeScope/AspNetCoreChildLifetimeScope.csproj index 3bd734b..19edf59 100644 --- a/src/AspNetCoreChildLifetimeScope/AspNetCoreChildLifetimeScope.csproj +++ b/src/AspNetCoreChildLifetimeScope/AspNetCoreChildLifetimeScope.csproj @@ -1,6 +1,7 @@ + ../../build/AspNet.ruleset net10.0 enable diff --git a/src/AspNetCoreExample/AspNetCoreExample.csproj b/src/AspNetCoreExample/AspNetCoreExample.csproj index 3bd734b..19edf59 100644 --- a/src/AspNetCoreExample/AspNetCoreExample.csproj +++ b/src/AspNetCoreExample/AspNetCoreExample.csproj @@ -1,6 +1,7 @@ + ../../build/AspNet.ruleset net10.0 enable diff --git a/src/AspNetCoreNoStartupExample/AspNetCoreNoStartupExample.csproj b/src/AspNetCoreNoStartupExample/AspNetCoreNoStartupExample.csproj index 3bd734b..19edf59 100644 --- a/src/AspNetCoreNoStartupExample/AspNetCoreNoStartupExample.csproj +++ b/src/AspNetCoreNoStartupExample/AspNetCoreNoStartupExample.csproj @@ -1,6 +1,7 @@ + ../../build/AspNet.ruleset net10.0 enable diff --git a/src/MultitenantExample.MvcApplication/MultitenantExample.MvcApplication.csproj b/src/MultitenantExample.MvcApplication/MultitenantExample.MvcApplication.csproj index e216e61..97745d1 100644 --- a/src/MultitenantExample.MvcApplication/MultitenantExample.MvcApplication.csproj +++ b/src/MultitenantExample.MvcApplication/MultitenantExample.MvcApplication.csproj @@ -1,5 +1,6 @@  + ../../build/AspNet.ruleset net481 Library bin/ diff --git a/src/MultitenantExample.WcfService/MultitenantExample.WcfService.csproj b/src/MultitenantExample.WcfService/MultitenantExample.WcfService.csproj index 981c67d..83b698a 100644 --- a/src/MultitenantExample.WcfService/MultitenantExample.WcfService.csproj +++ b/src/MultitenantExample.WcfService/MultitenantExample.WcfService.csproj @@ -1,5 +1,6 @@  + ../../build/AspNet.ruleset net481 Library bin/ diff --git a/src/MvcExample/MvcExample.csproj b/src/MvcExample/MvcExample.csproj index 74ad47b..afac154 100644 --- a/src/MvcExample/MvcExample.csproj +++ b/src/MvcExample/MvcExample.csproj @@ -1,5 +1,6 @@  + ../../build/AspNet.ruleset net481 Library bin/ diff --git a/src/WcfExample/WcfExample.csproj b/src/WcfExample/WcfExample.csproj index 4ce3a4e..8d82e6d 100644 --- a/src/WcfExample/WcfExample.csproj +++ b/src/WcfExample/WcfExample.csproj @@ -1,5 +1,6 @@  + ../../build/AspNet.ruleset net481 Library bin/ diff --git a/src/WebFormsExample/WebFormsExample.csproj b/src/WebFormsExample/WebFormsExample.csproj index 9f65ffc..88a6004 100644 --- a/src/WebFormsExample/WebFormsExample.csproj +++ b/src/WebFormsExample/WebFormsExample.csproj @@ -1,5 +1,6 @@  + ../../build/AspNet.ruleset net481 Library bin/