2

I'm trying to figure out which CallerMemberName or CallerFilePath value will be used if I'll apply these attributes to some service and then inject it into DI. For example:

public class MyService: IMyService
{
    public MyService([CallerMemberName] string name = null)
    {
        var name = name; // name is always null here
    }
}

...

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IMyService, MyService>();
    }
}

So, I guess, is it expected value for the name variable or am I doing something wrong? How do I work with CallerMemberName in that case? Is it possible, anyway?

anatol
  • 1,680
  • 2
  • 24
  • 47
  • The compiler supplies the value for `CallerMemberName` at compile time, wherever you call that method (or constructor in this case). Your DI framework calls the constructor using reflection at runtime, so it won't supply those values. What exactly are you trying to do? You won't be able to obtain the class that is requesting `IMyService`, because that class is only instantiated _after_ `IMyService` is resolved (and the eventual other dependencies of said class). – CodeCaster Feb 23 '21 at 11:18
  • @CodeCaster so, in other words, compiler doesn't supply any value because it doesn't know it? – anatol Feb 23 '21 at 11:20
  • Exactly, the compiler sees no call to `new MyService()`, because your code doesn't contain any. So again, in this scenario, _what_ caller do you wish to see, exactly? – CodeCaster Feb 23 '21 at 11:21
  • 2
    If you move the `CallerMemberName` usage to *methods you call* rather than the constructor, then you'd be able to see the caller at the point of the call. – Jon Skeet Feb 23 '21 at 11:30

1 Answers1

6

I'm trying to figure out which CallerMemberName or CallerFilePath value will be used if I'll apply these attributes to some service and then inject it into DI.

You can't. Your approach won't work because the default DI system used by ASP.NET Core (Microsoft.Extensions.DependencyInjection) uses dynamic registration which means neither the AOT (C#-to-IL) nor JIT (IL-to-x64) compiler knows anything about the consumers of a DI service (note that though the registration is conceptually static and can only be performed at startup - it's all just an illusion: you can easily hack the default DI container at runtime).

As a sidebar: The only time where your approach would work was if you had some kind of code-generation system to create your object-factories and service-factories at build-time without using any runtime reflection or IL generation. In practice the only places you see that approach used it in unit-testing code where the tests need to strictly control every dependency - but they're often written by hand and it's very tedious and brittle. With Roslyn Code Generation I hope to see static service factories start to be used more because it gives you compile-time guarantees that every dependency is available - that way you don't get any nasty surprises at runtime about missing dependencies.

The [CallerMemberName] and [CallerFilePath] attributes are populated by the AOT compiler, not the JIT compiler, and even if it was by the JIT compiler it won't help because a DI service's consumer is not the caller of a service's constructor.

am I doing something wrong?

You are. For the reasons I described above.

How do I work with CallerMemberName in that case?

You can't. CallerMemberNameAttribute is not intended for this purpose. It's intended for quick-and-easy logging, profiling, and tracing.

Is it possible, anyway?

Yes - by extending Microsoft.Extensions.DependencyInjection correctly. Please read the MEDI documentation for more details.

Dai
  • 141,631
  • 28
  • 261
  • 374