Repository navigation
Modernize Examples for the current release wave #32
Description
I just shipped updated versions across all the Autofac libraries, and this repo has fallen behind far enough that several examples no longer reflect what we actually publish. This issue tracks getting it current, gating it so it stays current, and closing the biggest coverage gaps.
Why this is more than a version bump
Three findings from the audit shape the work:
The .NET Framework integration packages target net481 now. Confirmed at the release tags — Autofac.Mvc5 v7.0.0, Autofac.Wcf v8.0.0, Autofac.Web v8.0.0, Autofac.Owin v8.0.0. The six net472 projects in here cannot take current packages without retargeting. They are also the furthest behind: still on Autofac 6.5.0 against a current 9.3.2.
Four org repos have no published package, so they can't have copy-pasteable examples: Autofac.Analyzers, Autofac.AspNetCore, Autofac.Extensions.Hosting, Autofac.Extras.AggregateService.SourceGenerator. Notably that rules out using our own analyzer package to raise code quality in here.
dotnet format fails today — missing UTF-8 BOM in the three ASP.NET Core projects, import ordering, final newlines, and one whitespace error. All auto-fixable, but it means adding the CI gate and fixing the code have to land together.
Two bugs also turned up that have nothing to do with modernization:
WebFormsExample.csprojsetsRootNamespaceandAssemblyNametoMvcExample. Combined withOutputPath=bin/andAppendTargetFrameworkToOutputPath=false, its output collides with the real MvcExample.ci.ymltriggers ondevelopand gates pull requests againstdevelop, but this repo only hasmain— it's one of our non-Gitflow repos. PR builds never fire.
Decisions
Recording these so the tasks below don't get relitigated:
- Retarget the legacy examples to
net481rather than freezing them on Autofac 6.x. Examples that don't match what we ship are worse than no examples. - Keep CI local to this repo. The org's shared
ci.ymlassumesdefault.proj, a codecov upload, and NuGet publishing — none of which apply to a repo with no packages and no tests. Adding inputs for a single consumer isn't worth the conditional complexity. .editorconfigplusAnalysisLevel=latest-recommended, and no StyleCop. Nullable on, warnings-as-errors in Release. StyleCop wants file headers and XML docs on public members, which is a lot of ceremony wrapped around ten-line teaching samples.- No test projects except where testing is the point. The Moq and FakeItEasy examples are the only ones that should carry tests, and both are deferred below.
Work
Ordered, because they stack. Each lands as its own pull request against main.
- 1. Root config refresh. Replace
.editorconfigwith the current one from the core repo (ours is the pre-2020 version — no per-filetype sections, nodotnet_diagnosticseverities). Add.markdownlint.json; pre-commit runs markdownlint with no config today. Bump the markdownlint hook to v0.49.1 to match core. Relax theglobal.jsonpin from10.0.203to10.0.100so any 10.0 SDK can build this. Fix thedeveloptriggers inci.yml. Clean up the README: dead AppVeyor badge,Examples.slnshould beExamples.slnx, and it still advertises a ServiceFabric example that was deleted in cae30c6. Verify:pre-commit run --all-filesis clean. - 2. CI gates. Add
dotnet formatandpre-commitjobs tobuild.yml, mirroring the org workflow's job bodies minus thedefault.proj, codecov, and publish steps. The format job needswindows-latest—net481projects won't restore on Linux. Also port the org workflow'sgatejob: Refresh root configuration and normalize the repo #33 built twice for one commit, once for the branch push and once for the pull request, and adding two more jobs would triple that waste. Verify: all jobs run, fail loudly on a deliberate violation, and only one run happens per change. - 3. Dependency updates,
net10.0projects. Autofac and the integration packages to current, plusMicrosoft.Extensions.*. Split from the retarget below so anet481surprise can't block the modern examples. Verify:dotnet build Examples.slnx -c Releaseclean. - 4. Retarget
net472→net481. All six legacy projects, with every Autofac package to current, plusMicrosoft.AspNet.Mvc,Newtonsoft.Json,Microsoft.Bcl.AsyncInterfaces, andMicrosoft.NETFramework.ReferenceAssemblies. Verify:dotnet build Examples.slnx -c Releaseclean on Windows. - 5. Code standards pass. Add a root
Directory.Build.props— there is none today — carryingNullable,ImplicitUsings,EnforceCodeStyleInBuild,AnalysisLevel, andTreatWarningsAsErrorsin Release, so policy lives in one place instead of fifteen csproj files. Then fix the fallout: file-scoped namespaces everywhere (ccd9b35 claimed to do this but GenericHostBuilderExample, AttributeMetadataExample, and thenet472projects still use block scope), drop usings made redundant byImplicitUsings, remove the one remaining// ReSharpercomment, normalize the stray csproj blank lines, and fix the WebFormsExample assembly name bug. Applied as a scripted pass over the project list so the diffs are identical by construction. Verify: zero warnings in Release,dotnet format --verify-no-changesclean. - 6. VS Code integration.
launch.jsonstill has configurations forAspNetCore3ExampleandAspNetCore3ChildLifetimeScope, neither of which exists anymore, and every surviving one points at abin/Debug/net6.0/path for a project that targetsnet10.0. AspNetCoreChildLifetimeScope and AspNetCoreNoStartupExample do exist and have no configuration at all. Environment variables move out oflaunch.jsonand into the per-projectlaunchSettings.json— all eight web projects already have one — referenced from the launch configuration vialaunchSettingsProfileso there's a single place to set them. Switch toDOTNET_ENVIRONMENT; theASPNETCORE_-prefixed form is the older pattern. Only the three ASP.NET Core projects actually matter there, since classic ASP.NET reads neither prefix and the .NET Framework entries are inert.settings.jsonstill carriesomnisharp.*keys the Roslyn language server ignores, andextensions.jsonrecommends a test explorer for a repo with no tests. Depends on item 4, because the launch paths embed target frameworks. Verify: every configuration inlaunch.jsonstarts the project it names. - 7. Navigation. A table in the README — example, what it demonstrates, packages, link to the matching docs page. A short README per example: what it shows, how to run it, link to the doc page, and nothing else. Group
Examples.slnxinto solution folders by area instead of one flat/src/. Leaving thesrc/layout alone; restructuring it churns every inbound docs link for no gain once the table exists. Verify: everysrc/directory has a README. - 8. New examples. DynamicProxy interception, Autofac.Pooling, Autofac.Diagnostics.DotGraph, Autofac.AspNetCore.Multitenant, and a middleware example for More middleware examples Documentation#160. All
net10.0, all cross-platform. Verify: each builds and runs. - 9. Documentation repo follow-up. All ten cross-links into this repo point at
/tree/master/...and our default branch ismain— GitHub's rename redirect isn't something to rely on. Same pull request adds the middleware section that closes More middleware examples Documentation#160 and links the four new examples from their doc pages.
Middleware examples for autofac/Documentation#160
advanced/pipelines.rst covers the mechanics thoroughly, but its only sample is the Hello World logging lambda, and that issue asks for concrete real-world cases. Scenarios to cover, in priority order:
- Blanket AOP across every registration — the Hook/Extension point modifying IRegistrationBuilder Autofac#1337 case that issue cites. Wrapping every resolved service via the
Registeredevent orIServiceMiddlewareSource, with no per-registration extension call. This is the one that wasn't obvious, so it's the point. - Resolve timing and instrumentation — measuring activation cost per service.
- Short-circuiting — returning a cached instance without calling
next. - Ambient parameter injection — pushing a correlation ID or tenant into every activation through
ResolveRequestContext. - Fail-fast scope validation — service middleware that throws something readable when a service is resolved from the wrong scope.
Coverage gaps deferred
The goal is one example per non-deprecated library. After item 8, these are still uncovered:
| Package | Disposition |
|---|---|
Autofac.Extras.Moq |
Worth doing. xUnit test project — one of the two places tests belong here. |
Autofac.Extras.FakeItEasy |
Worth doing, same shape as Moq. |
Autofac.Extras.AggregateService |
Worth doing. Small console app. |
Autofac.Mef |
Worth doing. Small console app. |
Autofac.Mvc5.Owin |
Low value, and better as an addition to the existing OWIN self-host example than a new project. |
Autofac.SignalR2 |
Low value. net481, ASP.NET SignalR 2. Legacy, but it is a shipping package. |
Autofac.Extras.CommonServiceLocator |
Not planning to cover. CSL upstream is abandoned; not formally deprecated, but not worth teaching. |
Autofac.ServiceFabric |
Not planning to cover. Deliberately deleted in cae30c6; the SF SDK is Windows-only and isn't on GitHub runners, so any example we add here will rot. The tagged history still has the old one. |
Autofac.Analyzers, Autofac.AspNetCore, Autofac.Extensions.Hosting, Autofac.Extras.AggregateService.SourceGenerator |
Blocked. Unpublished, so an example wouldn't be runnable by anyone reading it. |
Separately, there are documented core features with no example anywhere — decorators and composites, keyed services, registration sources, delegate factories, owned instances, native AOT and trimming — and integration/blazor.rst and integration/azurefunctions.rst have docs with no example. That's a real gap against "one example per documented feature," but it's much larger in scope than one-per-library and belongs in its own issue.
Activity
tillig commentedon Aug 31, 2026
Item 1 done, on
feature/root-config-refreshThree commits: the config refresh itself, a repo-wide normalization, and a
.git-blame-ignore-revsentry for the latter.Two things turned up that weren't in the plan.
The pre-commit hooks had never been run across the whole repo. A
pre-commit run --all-filesautofixed 67 files. Most of that was vendored jQuery and MicrosoftAjax underScripts/, tool-generated WCF proxies underService References/, and Web Forms*.designer.cs— all of it tool output that gets rewritten the next time anything regenerates it. Normalizing that is churn we'd pay for repeatedly and it would bury the real diff, so those paths are now excluded at the top level of.pre-commit-config.yaml. That's a deliberate divergence from core, which has no vendored scripts or generated proxies to worry about. With the exclusions in place the normalization came down to 29 files we actually own: final newlines, trailing whitespace, and JSON key ordering, no behavior changes.That normalization is its own commit and is listed in
.git-blame-ignore-revs, same as the file-scoped namespace conversion in ccd9b35.ISSUE_TEMPLATE.mdneeded a top-level heading to satisfy MD041, which is now enforced since markdownlint finally has a config. I just added the heading, but the real question is whether this file should exist at all — a rootISSUE_TEMPLATE.mdoverrides the org-level templates inautofac/.github, and I'd rather this repo just inherit those. Leaving it for now; not going to fold an issue-template policy change into a config cleanup.One consequence worth flagging:
src/ConfigurationExample/autofac.jsonis now alphabetically sorted, which putsservicesbeforetypeand movesdefaultAssemblyto the bottom. It reads a little worse as a teaching sample than it did. I decided against excluding it — one file getting a pass from the sorter is a worse precedent than a slightly awkward key order.Verified with
pre-commit run --all-filesclean and a Release rebuild of all seven cross-platform projects. Thenet472projects can't be built on macOS, so those wait for CI.tillig commentedon Aug 31, 2026
#33 is open for item 1 and green. That build is on
windows-latestover the wholeExamples.slnx, so the six .NET Framework projects came through the normalization clean too — which I couldn't check locally.One thing CI made obvious that belongs in item 2: every change here builds twice. The push to
feature/root-config-refreshand the pull request event each kicked off a full run for the same commit. The org's sharedci.ymlhas agatejob precisely for this — it skips the push build when a feature branch already has an open PR — and since I've decided to keep CI local to this repo, item 2 needs to bring that logic over rather than just bolting on the format and pre-commit jobs. Otherwise adding two more jobs turns one wasted duplicate run into three.tillig commentedon Aug 31, 2026
Two additions from reviewing #33, plus one correction.
The correction: the VS Code work was in my original list of things to fix and I left it out of the checklist above when I wrote it up. It's now item 6, and items 7 through 9 shifted down by one.
DOTNET_overASPNETCORE_.DOTNET_ENVIRONMENTis the current pattern andASPNETCORE_ENVIRONMENTis the legacy prefix, so new and updated examples should use the former. Worth knowing before anyone runs a find-and-replace: there are 16 occurrences ofASPNETCORE_ENVIRONMENTin here and only three are meaningful. The other 13 sit in the .NET Framework projects'launchSettings.json, and classic ASP.NET reads neither prefix, so those are inert VS-generated boilerplate. Changing them would be churn that implies a behavior difference that doesn't exist.Environment variables belong in
launchSettings.json, notlaunch.json. This is a bigger cleanup than it sounds like, because all eight web projects already have alaunchSettings.jsonsettingASPNETCORE_ENVIRONMENT— solaunch.jsonhas been duplicating configuration that already existed, in a file that also happens to have the wrong paths and names two projects we deleted. Pointing the launch configurations at the profiles withlaunchSettingsProfilemeans one place to change an environment variable instead of two that can disagree.Both are folded into item 6.
Also updated item 2 to cover the duplicate-build gate, so the checklist stands on its own without reading the comments.
tillig commentedon Aug 31, 2026
Item 1 is merged in #33 (00a7954).
Confirmed the merge commit preserved the individual SHAs, so the
.git-blame-ignore-revsentry pointing at 39b6373 still resolves onmainand blame skips the normalization as intended. A squash merge would have quietly orphaned that hash and left the file pointing at a commit that no longer exists — worth remembering for any future PR that adds a blame-ignore entry.Item 2 is next: the format and pre-commit jobs plus the duplicate-build gate.
tillig commentedon Sep 1, 2026
Item 2 is up in #34 and green. The gate does what it's supposed to: the branch-push run skipped all three heavy jobs while the pull request run executed them, so one commit now produces one build instead of two.
Two things came out of it that affect later items.
Item 5 needs
LangVersionbefore it can do anything about nullable.dotnet formatconverted namespaces in thenet10.0projects only. The other 63 block-scoped files live innet472andnetstandard2.0projects, which default to C# 7.3, and file-scoped namespaces need C# 10 — so IDE0161 is correctly silent rather than being skipped by accident. The same wall sits in front of nullable reference types, which need C# 8. SoNullablecan't just go intoDirectory.Build.propsand apply everywhere; eitherLangVersiongets set for the .NET Framework projects or they sit out the nullable work. Retargeting tonet481in item 4 does not fix this on its own, because the language default tracks the framework.dotnet formatunder-reports on macOS for the .NET Framework projects. Running it againstMultitenantExample.WcfServicealone produced no diagnostics at all, with only a "warnings were encountered while loading the workspace" note, while the same projects did report diagnostics in a whole-solution run. Windows CI agreed with the solution-level result and passed, so the gate is trustworthy — but local runs on a Mac aren't authoritative for those six projects, and anyone working on items 4 through 6 should expect CI to be the real check.Also worth recording: the auto-property expansion in #34 is latent in core's
.editorconfig, not absent from it.csharp_preserve_single_line_blocks = falsedidn't fire in core only because 32 of its 34 single-line auto-properties have initializers and the one bare case is an interface indexer. The first bare{ get; }written in a class in core will expand the same way. I decided to take the expansion here rather than diverge the config, but that's worth knowing the next time someone is surprised by it.Rename the OWIN self-host base address constant to PascalCase
Apply dotnet format across the solution
Ignore the dotnet format commit in git blame
Gate CI on formatting and pre-commit, and stop double-building
tillig commentedon Sep 1, 2026
Item 3 is up in #35 and green. Every package on the
net10.0andnetstandard2.0side is current, and no source changes were needed.The one thing I expected to hurt didn't. AttributeMetadata 7.0.0 dropped
ParameterFilterAttribute,WithKeyAttribute,WithMetadataAttributeand theWithAttributeFilterextension, all of which had been obsolete and pointing at core. The example already used the coreAutofac.Features.AttributeFiltersequivalents, so it compiled and ran untouched. Anyone updating a real project across that boundary won't be so lucky.I ran every example rather than just building them, which was worth doing twice over. Multitenant went up a major version and a compile tells you nothing about whether tenant overrides still resolve to the right instances — they do, verified for tenant 1, tenant 2, and the default. And
src/ConfigurationExample/autofac.jsonwas alphabetically re-sorted in #33, so this was the first real confirmation that Autofac.Configuration still reads it afterdefaultAssemblymoved to the bottom. It does; both plugins resolve.Two notes for whoever picks up the remaining items:
AspNetCoreChildLifetimeScopehardcodesUseUrls("http://localhost:5000")and5001for its two hosts and ignoresASPNETCORE_URLS. That's fine for what it demonstrates, but it means the port can't be overridden from the environment, which is worth remembering when item 6 wires up launch profiles.The gate ran two full builds on this PR. That's correct behavior rather than a bug — I pushed the branch before opening the pull request, so at push time there was no open PR to defer to. Pushing after the PR exists, or opening it first, gets the dedupe. Later pushes to the same branch are skipped as intended.
Update the .NET 10 examples to current package versions
tillig commentedon Sep 1, 2026
Item 4 is up in #36 and green. All six projects are on
net481with current packages.The interesting part was binding redirects, and it's a trap worth documenting. Every Autofac integration package is compiled against Autofac
9.3.1.0while these projects now load9.3.2.0. On .NET Framework that mismatch is fatal at load time, and web projects get no auto-generated redirects the way an exe does —WebApiExample.OwinSelfHostgot a correctAutofacredirect for free in its generated.exe.config, while the five web apps had none at all. So the retarget would have produced a green build and five examples that throw at startup.Rather than guess the versions, I read them out of the built output with
System.Reflection.Metadata, walked everyAssemblyRefin each project'sbin, and flagged each reference whose version differs from the assembly actually shipping next to it. The resulting set matched what MSBuild generated for the self-host exe, which is a useful independent check. Four redirects needed changing (Microsoft.Bcl.AsyncInterfaces,System.Threading.Tasks.Extensions,System.Web.Mvc) or adding (Autofac).That process also surfaced a pre-existing one:
System.Web.Mvc,System.Web.Optimization, andSystem.Web.WebPagesall referenceMicrosoft.Web.Infrastructure 1.0.0.0while2.0.0.0ships. Nothing to do with the retarget, but three examples wouldn't start without it. It's a separate commit so it can be reviewed on its own merits.What is not verified: any of this at runtime. These are .NET Framework web and self-host apps, they can't run on macOS, and CI only builds them. The redirect work is derived from assembly metadata, not observed at startup. Before we treat item 4 as truly done, someone should launch one MVC example and one WCF example under IIS Express on Windows and confirm they come up. If they don't, the fix is almost certainly one more missing redirect rather than anything structural.
Confirmed for item 5:
LangVersiondefaults to7.3on thenet481andnetstandard2.0projects and14.0onnet10.0. The retarget did not change this, as expected. So item 5 has to setLangVersionexplicitly before nullable or file-scoped namespaces can reach the eight non-net10.0projects, or accept that those projects sit out both.Sort binding redirects by assembly name
tillig commentedon Sep 1, 2026
Item 5 is up in #37 and green. All 15 projects build with zero warnings in Release, down from 218.
LangVersion=latestturned out to be load-bearing rather than cosmetic.Nullable=enableunder C# 7.3 is a hard build error,CS8630, so a globalNullableproperty would have broken 8 of the 15 projects outright. Raising it also unblockedIDE0161, which is the real reason file-scoped namespaces only ever reached thenet10.0projects:dotnet formathad been silently unable to touch the other 57 files for the last four pull requests. They're converted now. The six still on block scope are tool-generated designer and WCF proxy files that Roslyn excludes from style rules by design, which is the same set the pre-commit exclusions already skip.One real defect came out of it. CA3147 flagged the MVC
AboutandIndexactions for missing antiforgery validation, and the reason is that neither had[HttpGet], so both also accepted POST. Adding the attribute is the fix; putting an antiforgery token on a read-only view would have been theatre.Five rules are suppressed, documented inline in the props file. CA1707 and CA1716 are names ASP.NET dictates —
_DefaultforDefault.aspx,Page_LoadforAutoEventWireup,GlobalforGlobal.asax. CA1848 and CA1873 are logging-performance boilerplate, which is correct in production and pure noise around a two-line sample; avoiding that was the whole point of pickinglatest-recommendedoverlatest-all. CA5368 wants a per-userViewStateUserKeyand these samples have no user identity to supply one.NU1900is excluded fromTreatWarningsAsErrors. It reports the vulnerability audit feed being unreachable and it fired locally as a 403. Turning a feed outage into a failed build is not a trade worth making, and it's the kind of thing that would have shown up as a mystery red CI weeks from now.Two notes for later items:
The multitenant console app needed a small refactor, not just annotations — it kept its container and tenant strategy in nullable statics assigned from
Main, so every use demanded a null check the design never actually allowed. They're readonly fields initialized at their declaration now, which also cleared CA1859. I re-ran it to confirm tenant 1, tenant 2, and default still resolve to distinct overrides, since moving initialization out ofMainchanges when the container gets built.EnforceCodeStyleInBuilddoes not make IDE0005 fire, so unused usings are still not enforced. I removed the 25 that existed by comparing each file against the SDK's generated global usings, but nothing stops them coming back. Enforcing it needsGenerateDocumentationFile, which would then demand XML docs on public members across every sample — not worth it, but worth knowing it isn't gated.Govern analyzer rules with rulesets instead of NoWarn
Correct the category on the _Default naming suppression
tillig commentedon Sep 1, 2026
Item 6 is up in #38 and green.
launch.jsonwas worse than "some stale entries" — nothing in it could start. Two configurations named projects deleted long ago, and every remaining one pointed at anet6.0ornetcoreapp3.1path for a project targetingnet10.0. Two runnable projects had no configuration at all. It's now seven configurations for seven launchablenet10.0projects, with the environment variables coming fromlaunchSettings.jsonvialaunchSettingsProfileinstead of a duplicate inlineenvblock.Rather than eyeball it, I validated the file programmatically: every configuration names a real project, its
programexists on disk and sits under its declaredcwd, its profile resolves to a profile that exists, and nothing retains an inlineenv, a stale target framework, or asourceFileMap. Run in reverse too — every launchable project has a configuration and no configuration names a missing project. Worth keeping that check in mind for whoever adds the new examples in item 8, because a new sample with no launch configuration is the exact drift that got us here.Two things I confirmed rather than assumed, since the ASP.NET Core templates still emit
ASPNETCORE_ENVIRONMENT:DOTNET_ENVIRONMENTis honoured by these apps, and the value genuinely arrives from the launch profile — with both variables cleared from the shell, running through the profile reportsHosting environment: Development.A caution for anyone smoke-testing these locally. I nearly recorded a false pass:
AspNetCoreChildLifetimeScopehardcodes ports 5000 and 5001, and a stray process from an earlier test was still holding 5000. The run under test failed to bind, but curl happily got a valid response from the stale process. Kill leftover listeners between runs, and don't trust an endpoint response as proof the process you just started is the one answering.Also removed the
omnisharp.*settings, which the Roslyn language server ignores, a test explorer recommendation for a repo with no tests, and asourceFileMappointing at aViewsfolder that doesn't exist at the workspace root. Addeddotnet.defaultSolutionso the C# extension stops asking which solution to open.Open question: none of the six .NET Framework projects have a launch configuration. The five web ones need IIS Express, and
WebApiExample.OwinSelfHostneeds the Windows-onlyclrdebugger instead ofcoreclr. I left them out rather than ship configurations that fail confusingly on macOS and Linux, but aclrconfiguration for the self-host example would work fine on Windows if we want it.tillig commentedon Sep 1, 2026
Item 7 is up in #39 and green. Every example has a README and the root README carries an index table grouped into ASP.NET Core, hosting and core features, and .NET Framework.
Examples.slnxgroups by the same three areas.I left the
srclayout alone on purpose. Reorganising directories would break all ten inbound links from the Documentation repo, and the table solves the browsability problem without touching a single path.Worth noting for item 9: the doc links in these READMEs are validated by mapping each
readthedocs.io/en/latest/<path>.htmlback todocs/<path>.rstin the Documentation repo and confirming the file exists. That's a cheap offline check that catches a renamed or misspelled page, and it's the same mapping item 9 needs in reverse when adding "see the example" links back from the docs. All 32 links in this PR check out.The run instructions were executed rather than written from the launch profiles. Both
/api/valuesexamples bind port 5000 and return["value1","value2"], and the child lifetime scope example serves ApplicationA on 5000 and ApplicationB on 5001. I also grepped each log forHosting failed, having already been caught once by a stale listener answering curl after the process under test failed to bind.One thing that shaped the writing: the three ASP.NET Core examples all point at the same documentation page and differ only in hosting shape. Three READMEs summarising "Autofac with ASP.NET Core" would have been useless, so each one says how it differs from its siblings instead. If we add more near-duplicate examples later, that framing is the thing that makes them navigable.
Link package names to the repositories that own them
tillig commentedon Sep 1, 2026
First half of item 8 is up in #40 and green: examples for DynamicProxy, Pooling, DotGraph, and multitenant ASP.NET Core. The middleware example for autofac/Documentation#160 follows separately, alongside the documentation changes it needs, which keeps item 9 and that half of item 8 in one reviewable pair.
Four things learned building these that are worth having written down.
Autofac.AspNetCore.Multitenant ships no tenant identification strategy. Its README shows a
QueryStringTenantIdentificationStrategyas though it were part of the package; it isn't, and the reader has to write one. I checked the public surface rather than trusting the README. The example now supplies its own, which is arguably the more useful thing to demonstrate anyway.Pooling looks exactly like SingleInstance unless the example proves otherwise. The distinguishing behaviour is that sequential scopes share an instance while overlapping scopes don't, and that a policy can reset state on return. So the example prints a line per construction and reports the use count after reuse: seeing
1rather than2is the only visible evidence the reset ran. Worth remembering if anyone rewrites that sample more tersely -- the verbosity is the point.The two multitenant packages disagree on namespace conventions.
AutofacMultitenantServiceProviderFactorylives inMicrosoft.AspNetCore.Hosting, not underAutofac.*, so it arrives via the Web SDK's implicit usings and my first attempt at an explicit using failed to compile.A
.dotfile that looks right may not parse. Graphviz is installed locally, so I rendered the captured graph rather than eyeballing it. If we add more diagnostics examples, actually rendering the output is cheap and catches malformed traces.All 19 projects build clean in Release under the #37 analyzer rules, every example was run rather than only compiled, and the #38 launch-configuration validator reports 11 configurations for 11 launchable projects with nothing missing or orphaned.
Rename AspNetCoreMultitenantExample to MultitenantExample.AspNetCore
Sort launch configurations and split the DotGraph services into one f…
Document each middleware scenario and fix comment spacing
tillig commentedon Sep 1, 2026
All nine items are done. Nine pull requests here (#33 through #41) plus two in the Documentation repo (autofac/Documentation#205, and the example links folded into autofac/Documentation#204).
Where the repository ended up:
latest-recommendedanalyzers, and code style enforced at build. It was 218 warnings when the rules first went in.dotnet formatandpre-commit, and the portedgatejob means one commit produces one build instead of two.net481because their integration packages moved there.Four findings worth keeping in mind, because they will each bite again:
A green build does not mean a .NET Framework example runs. Every Autofac integration package is compiled against Autofac
9.3.1.0while the examples load9.3.2.0, and web projects get no auto-generated binding redirects the way an exe does. #36 added them by reading assembly metadata out of the built output, but CI only compiles. Nobody has yet started one of these under IIS Express on Windows. Until someone does, treat the .NET Framework half as build-verified only.Unused usings are not enforced.
EnforceCodeStyleInBuilddoes not make IDE0005 fire; that needsGenerateDocumentationFile, which would then demand XML docs on every public member. I removed the 25 that existed and found another dead one by hand in #41, so they do come back.dotnet formatunder-reports on macOS for thenet481projects — running it against a single one of them produces no diagnostics at all. The whole-solution run on Windows CI is the authoritative check.A stale listener will answer curl. Two examples hardcode port 5000, so a leftover process from an earlier run can serve a request and make a failed start look like a pass. Check the log for
Hosting failed, not just the response.Closing this. The remaining example gaps are tracked separately.
tillig commentedon Sep 1, 2026
Correction to the comment above: I said the remaining gaps were tracked separately, and they are not — no follow-up issues were opened. They are recorded in the disposition table in this issue's description instead, along with the notes on what is worth doing and what deliberately is not, so closing this does not lose them.
The one item I would not want buried: the six .NET Framework examples have never been started under IIS Express. Their binding redirects were derived from assembly metadata rather than observed at runtime, and CI only compiles, so that half of the repository is build-verified only.
Closing. Happy to pick up the deferred examples, or the larger gap around core features with no example at all, whenever it comes back around.