Keith
Keith

Reputation: 155882

.NET command line dependency injection fails when called from path

I have a command line app that uses .NET's dependency injection.

It works fine when called in its directory, like C:\GitHub\MyProject\bin\Debug\net9.0>MyApp

However, when I add it to the Path environment variable and call it from another location it fails:

C:\Users\me>MyApp

Unable to resolve service for type 'MyNamespace.IDependedOnService' while attempting to activate 'MyNamespace.Service'

Both MyNamespace.IDependedOnService and MyNamespace.Service are in my code, added in builder.ConfigureServices(..., but none of that code is in the stack trace.

In fact, the only non Microsoft code in the entire error stack is:

at Program.<Main>$(String[] args) in C:\GitHub\MyProject\Program.cs:line 83

Which is:

using var host = builder.Build();

After much digging it looks like it's picking up config settings in the secrets file, but missing any in C:\GitHub\MyProject\bin\Debug\net9.0\appsettings.config

Why does it only find the settings when run from the same directory?

How should .NET console apps be configured when run from the Path?

Upvotes: 0

Views: 33

Answers (2)

norekhov
norekhov

Reputation: 4318

From a .NET point of view it doesn't really matter where your executable file is physically. And it is absolutely correct.

Consider Linux for example. Executables are stored in /usb/sbin but configuration is in your home folder.

You can have one application installed but launched by different users. One binary multiple configurations.

Another example is when you make a single file executable. In this case from my understanding Assembly.Location will become even more irrelevant.

Moreover storing configuration next to binaries can be a vulnerability. In Windows "Program Files" are readable to all users. Ideally your app is not launched under Admin account and therefore can read but can't write there.

So this is why i guess

Upvotes: 0

Keith
Keith

Reputation: 155882

I think the problem is actually in:

// Init .NET DI host
var builder = Host.CreateDefaultBuilder(args);

As from its documentation:

set the IHostEnvironment.ContentRootPath to the result of Directory.GetCurrentDirectory()

In this case Directory.GetCurrentDirectory() returns C:\Users\me.

The fix is to set the working directory to be the app directory before creating the builder:

Directory.SetCurrentDirectory(AppContext.BaseDirectory);

var builder = Host.CreateDefaultBuilder(args);

...

// Now works
using var host = builder.Build();

This still feels clunky and I'd welcome a better answer/best practice for command line configuration.

Upvotes: 0

Related Questions