Skip to content

fix(filetransfer): register a default no-op IFileProvider - #23

Merged
mrdevrobot merged 1 commit into
mainfrom
fix/filetransfer-default-provider
Sep 6, 2026
Merged

fix(filetransfer): register a default no-op IFileProvider#23
mrdevrobot merged 1 commit into
mainfrom
fix/filetransfer-default-provider

Conversation

@mrdevrobot

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes AddEntglDbFileTransfer forces every consumer to implement IFileProvider, even download-only clients #22: AddEntglDbFileTransfer() forced every consumer to implement IFileProvider, even a pure download-only client, or hit an unobserved InvalidOperationException inside a fire-and-forget node.Start() call - silently never starting the node (no listener, no logs, no crash).
  • Added NullFileProvider (reports every file as not found), registered via TryAddSingleton<IFileProvider, NullFileProvider>() inside AddEntglDbFileTransfer().
  • A node that wants to serve files still registers its own provider afterward via plain AddSingleton<IFileProvider, MyProvider>() - since the handlers depend on a single (non-enumerable) IFileProvider, the last registration wins over the library's default.

Test plan

  • dotnet build/dotnet test on EntglDb.Services.FileTransfer
  • Regression: a consumer that DOES register its own IFileProvider after AddEntglDbFileTransfer() still resolves to that provider, not NullFileProvider
  • New: a consumer that registers no IFileProvider at all now starts cleanly and reports every file query as not found instead of throwing

🤖 Generated with Claude Code

AddEntglDbFileTransfer() unconditionally registers FileQueryHandler
and FileDownloadHandler as INetworkMessageHandler, and both require
IFileProvider in their constructor with no default. A node that only
ever downloads files (never serves any) had to hand-write and
register its own no-op IFileProvider just to satisfy that dependency
- otherwise resolving IEnumerable<INetworkMessageHandler> during
IEntglDbNode.Start() throws.

That failure is especially dangerous because Start() is commonly
invoked as a fire-and-forget task (`_ = node.Start()`) during app
startup: the exception goes unobserved, and the node never finishes
starting - no TCP listener, no logs, no crash, nothing to grep for.

Added NullFileProvider, registered via TryAddSingleton inside
AddEntglDbFileTransfer() itself. A download-only consumer now needs
nothing further. A node that does want to serve files still registers
its own provider afterward via plain AddSingleton; since
FileQueryHandler/FileDownloadHandler depend on a single (non-
enumerable) IFileProvider, the last registration wins.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 6, 2026 16:29
@mrdevrobot
mrdevrobot merged commit 8043b1c into main Sep 6, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A couple of newly added behaviors/docs are misleading/incomplete (notably cancellation-token handling and the rationale/documentation around TryAddSingleton), which should be corrected before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR fixes a DI startup failure in EntglDb.Services.FileTransfer by providing a default IFileProvider so download-only consumers can call AddEntglDbFileTransfer() without needing to implement server-side file serving.

Changes:

  • Added NullFileProvider, a default IFileProvider implementation that reports all files as not found.
  • Registered IFileProvider via TryAddSingleton<IFileProvider, NullFileProvider>() inside AddEntglDbFileTransfer() to avoid forcing consumers to register a provider.
File summaries
File Description
src/EntglDb.Services.FileTransfer/NullFileProvider.cs Introduces a default no-op provider implementation for download-only nodes.
src/EntglDb.Services.FileTransfer/FileTransferExtensions.cs Registers NullFileProvider by default and updates XML docs to describe the behavior.
Review details

Suppressed comments (1)

src/EntglDb.Services.FileTransfer/FileTransferExtensions.cs:57

  • The inline comment explains TryAddSingleton as necessary so a later AddSingleton becomes the “last registration”, but that would also be true if this were AddSingleton. The key reason for TryAddSingleton is to avoid overriding a caller-provided IFileProvider that was registered before AddEntglDbFileTransfer(). Updating the comment will prevent future refactors from reintroducing the issue.
        // Server-side default so a download-only consumer doesn't have to implement IFileProvider itself
        // just to satisfy FileQueryHandler/FileDownloadHandler's constructor dependency - see remarks
        // above. Must be TryAddSingleton (not AddSingleton) so a real provider the caller registers
        // afterward via plain AddSingleton becomes the winning "last registration" for the single-instance
        // resolution FileQueryHandler/FileDownloadHandler actually use.
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +17 to +21
public Task<FileTransferInfo?> GetInfoAsync(string fileId, CancellationToken ct = default) =>
Task.FromResult<FileTransferInfo?>(null);

public Task<Stream?> OpenReadAsync(string fileId, CancellationToken ct = default) =>
Task.FromResult<Stream?>(null);
Comment on lines +30 to +39
/// A node that only ever downloads files (never serves any) needs nothing further - it uses the
/// registered <see cref="NullFileProvider"/> default, reporting every file as not found. To make files
/// available for remote download instead, register a real provider <em>after</em> this call:
/// <code>
/// services.AddEntglDbFileTransfer();
/// services.AddSingleton&lt;IFileProvider, MyFileProvider&gt;();
/// </code>
/// <see cref="FileQueryHandler"/>/<see cref="FileDownloadHandler"/> take a single (non-enumerable)
/// <see cref="IFileProvider"/>, so the last registration wins - the call above's own provider overrides
/// the default even though both end up registered.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AddEntglDbFileTransfer forces every consumer to implement IFileProvider, even download-only clients

2 participants