Troubleshooting
Practical problem/solution guide for Pragmatic.Abstractions. Each section covers a common issue, the likely causes, and the fix.
Type Not Found After Adding Abstractions Reference
Section titled “Type Not Found After Adding Abstractions Reference”You added a <ProjectReference> or <PackageReference> to Pragmatic.Abstractions, but types like ICurrentUser or IClock are not resolving.
Checklist
Section titled “Checklist”-
Check the namespace. Types live under
Pragmatic.*, notPragmatic.Abstractions.*. Useusing Pragmatic.Identity;forICurrentUser,using Pragmatic.Temporal.Clock;forIClock, and so on. -
Check the project target framework. Abstractions targets
net10.0. If your project targets an earlier framework, the reference will not resolve. -
Rebuild the solution. After adding a project reference, Visual Studio sometimes needs a full rebuild to update IntelliSense. Run
dotnet buildfrom the command line to verify. -
Check for conflicting references. If your project references both
Pragmatic.Abstractionsand a full module (e.g.,Pragmatic.Identity), both expose types inPragmatic.Identity. This is intentional and works correctly — the full module re-exports the abstractions. But if you reference incompatible versions, you may get type mismatch errors.
”Ambiguous Reference” Between Abstractions and a Module
Section titled “”Ambiguous Reference” Between Abstractions and a Module”You see a compiler error like: 'ICurrentUser' is an ambiguous reference between 'Pragmatic.Identity.ICurrentUser' and 'Pragmatic.Identity.ICurrentUser'.
Two different assembly versions define the same type in the same namespace. This happens when:
- Your project references
Pragmatic.Abstractionsexplicitly AND a module that transitively references a different version of Abstractions. - NuGet package versions are out of sync.
-
Align versions. All Pragmatic packages are versioned together. Ensure every
Pragmatic.*reference uses the same version. -
Remove the explicit Abstractions reference. If your project references a full module (e.g.,
Pragmatic.Actions), you do not need to also referencePragmatic.Abstractions. The module transitively includes it. -
Check
Directory.Build.props. The root build configuration pins versions. Verify that no project overrides the version locally.
Null-Object Not Registered — Service Returns Null
Section titled “Null-Object Not Registered — Service Returns Null”You inject ICurrentUser or ITenantContext into a service, but it resolves to null at runtime (not the null-object singleton).
No module has registered a concrete implementation or fallback. Null-objects like AnonymousUser.Instance are not auto-registered — they are available for manual registration or direct use in code.
Option A: Register the null-object as a fallback.
services.AddSingleton<ICurrentUser>(AnonymousUser.Instance);services.AddSingleton<ITenantContext>(UnresolvedTenantContext.Instance);When the authentication middleware or tenant resolver runs, it replaces the singleton with a scoped registration for the real implementation. The null-object serves as the fallback when no middleware is configured.
Option B: Use the null-object directly in code.
var user = serviceProvider.GetService<ICurrentUser>() ?? AnonymousUser.Instance;Option C: Use Pragmatic.Composition. When using PragmaticApp.RunAsync(), the auto-registration in the generated host registers sensible defaults for all detected modules.
ICacheStack Not Available — Module Needs Caching
Section titled “ICacheStack Not Available — Module Needs Caching”A module depends on ICacheStack (defined in Abstractions), but no implementation is registered at runtime.
The ICacheStack interface lives in Abstractions so that modules like Authorization, Configuration, and Endpoints can depend on it without referencing Pragmatic.Caching. But the interface alone does not provide an implementation.
-
Add
Pragmatic.Cachingto the host project.<PackageReference Include="Pragmatic.Caching" /> -
Register caching in startup.
// With Pragmatic.Compositionapp.UseCaching(cache => cache.AddHybridCache());// Without Compositionservices.AddPragmaticCaching(); -
For tests: Create a mock or use
NullCacheStackif your test does not need real caching:var mockCache = Substitute.For<ICacheStack>();
Attribute Not Detected by Source Generator
Section titled “Attribute Not Detected by Source Generator”You applied [Service], [Module], or another composition attribute, but the SG does not generate any code.
Checklist
Section titled “Checklist”-
Is the source generator referenced? Your project needs the
Pragmatic.SourceGeneratoranalyzer reference:<ProjectReference Include="...\Pragmatic.SourceGenerator.csproj"OutputItemType="Analyzer"ReferenceOutputAssembly="false" /> -
Is the class
partial? Many SG outputs require the class to bepartial(e.g., entities, endpoints, actions). Check for PRAG diagnostics in the build output. -
Is the attribute from the correct namespace? Composition attributes live in
Pragmatic.Composition.Attributes. Ensure you are using the correctusingstatement. -
Does the project reference Abstractions (directly or transitively)? The attribute types must be available in the compilation. If Abstractions is not referenced, the attribute resolves to nothing and the SG skips the class.
ICallContext.IsInternalCall Always False
Section titled “ICallContext.IsInternalCall Always False”Event handlers or cross-boundary action calls are being blocked by authorization filters because IsInternalCall is false.
ICallContext is the abstraction. The implementation (ActionCallContext) lives in Pragmatic.Actions. If Actions is not referenced by the host, no ICallContext is registered, and the default behavior is “not internal.”
-
Ensure
Pragmatic.Actionsis referenced by the host and the DI registration includesActionCallContext. -
With Pragmatic.Composition: This is auto-registered when the SG detects Actions. Verify that the host includes the Actions module.
-
For manual registration:
services.AddScoped<ICallContext, ActionCallContext>();
Default Interface Implementation Not Working
Section titled “Default Interface Implementation Not Working”You added a new member to an interface with a default implementation, but existing implementations report a compile error.
Possible Causes
Section titled “Possible Causes”-
Target framework too old. Default interface implementations require C# 8+ and .NET Core 3.0+. Verify your project targets
net10.0or later. -
Implementation explicitly declares the member. If an existing class declares the member (even with the same signature), it overrides the default. If the declared signature does not match, the compiler reports an error.
-
Struct implementation. Default interface implementations are not supported on value types. If the type implementing the interface is a
struct, the member must be explicitly implemented.
Telemetry Tags Not Appearing in Traces
Section titled “Telemetry Tags Not Appearing in Traces”You are using ActivityHelper and tag constants from Pragmatic.Telemetry.Conventions, but tags do not appear in your observability backend.
Checklist
Section titled “Checklist”-
Is an ActivityListener configured?
System.Diagnostics.Activityevents are only captured when a listener is registered. In ASP.NET Core, OpenTelemetry registers listeners. In console apps, you must register one explicitly. -
Is the Activity non-null?
ActivityHelpermethods are null-safe — they silently no-op when the activity is null. If no listener is active,Activity.Currentis null. This is by design (zero overhead when not observed), but it means tags are silently dropped. -
Are you using the correct tag names? Use constants from
Pragmatic.Telemetry.Conventions(e.g.,ActionTags.Name,DbTags.Operation) instead of hand-typed strings. Mismatched tag names will not correlate in your backend.
When should I reference Abstractions directly versus through a module?
Section titled “When should I reference Abstractions directly versus through a module?”Reference Abstractions directly when:
- You are building a module runtime package that only needs contracts.
- You are writing a domain service library that depends on interfaces but not implementations.
- You are writing tests and want to mock interfaces without importing runtime dependencies.
Reference through a module when:
- You are building a host or application that needs both the contract and the implementation.
- A module already transitively provides Abstractions (no need to add it again).
Can I add new types to Abstractions?
Section titled “Can I add new types to Abstractions?”Yes, if the type meets all criteria in the checklist: consumed by 2+ modules, no implementation logic, no framework dependency, and stable. New types are additive and non-breaking.
Why are the Microsoft.Extensions.*.Abstractions packages allowed?
Section titled “Why are the Microsoft.Extensions.*.Abstractions packages allowed?”IPragmaticBuilder exposes IServiceCollection, IConfiguration, and IHostEnvironment. These three types come from Microsoft.Extensions.*.Abstractions packages which are:
- Part of the .NET platform (not ASP.NET Core).
- Stable, widely-used contracts.
- Lightweight with no runtime coupling.
- Used by non-web hosts (console apps, workers, Lambda functions).
Why do null-objects have private constructors?
Section titled “Why do null-objects have private constructors?”To enforce the singleton pattern. AnonymousUser.Instance is the only way to obtain an AnonymousUser. This prevents accidental creation of multiple instances and ensures identity equality checks (ReferenceEquals) work correctly.
Can I override a null-object in DI?
Section titled “Can I override a null-object in DI?”Yes. DI uses last-registration-wins. Register your implementation after the null-object:
services.AddSingleton<ICurrentUser>(AnonymousUser.Instance); // Fallbackservices.AddScoped<ICurrentUser, ClaimsPrincipalUserAccessor>(); // Wins at runtimeWhy is ServiceCollectionDecorateExtensions in Abstractions?
Section titled “Why is ServiceCollectionDecorateExtensions in Abstractions?”So that domain modules can use the decorator pattern (services.Decorate<TService, TDecorator>()) without referencing ASP.NET Core or Pragmatic.Composition.Host. The extension method only needs IServiceCollection (from Microsoft.Extensions.DependencyInjection.Abstractions), which is an allowed dependency.
How do I find which module implements a specific interface?
Section titled “How do I find which module implements a specific interface?”Each interface section in the README includes an “Implemented by” note. For example, ICurrentUser is implemented by Pragmatic.Identity.AspNetCore (ClaimsPrincipalUserAccessor). The interfaces.md document provides the same information with full member signatures.
Getting Help
Section titled “Getting Help”- GitHub Issues: github.com/pragmatic-design/Pragmatic.Design/issues
- Interface Catalog: See interfaces.md for complete member-level signatures.
- Design Principles: See design-principles.md for the full decision framework on what belongs in Abstractions.
- Concepts: See concepts.md for architecture, layer model, and interface catalog overview.