Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typed Services #2

Open
dazinator opened this issue Mar 9, 2020 · 2 comments
Open

Typed Services #2

dazinator opened this issue Mar 9, 2020 · 2 comments
Labels
enhancement New feature or request idea

Comments

@dazinator
Copy link
Owner

dazinator commented Mar 9, 2020

Resolving services at runtime by name is a known anti-pattern. That is not to say it isn't a useful feature that solves certain problems. The reason it is an antipattern is that:

  • resolution of the service with a specific name can fail at runtime if the service with that name has not been registered
  • the dependencies are not visible in any api surface - and in essence are therefore "hidden" unless you read the internals of the code.
  • due to above points, there is no way to check / validate that required dependencies are present when the class is constructed - which means it can technically run in an invalid state and hit a runtime exception (back to first point above).

Consider this:

public class Foo
{
  public Foo(Func<string, AnimalService> namedServices)
 {
      var one = namedServices("APOWDJAWOJD");
      var two = namedServices("TAWD");
 }
}

From the perspective of a consumer of this class, it knows there is some dependency on factory function that returns an AnimalService but it's not obvious what name is going to be used when a service is requested, therefore how can it establish how to implement the factory function, unless it can see the internal code in Foo class to work out what names will be used:

var foo = new Foo((name)=>{  // what am I meant to do here? what names will be used? })

So this dribbles down a problem when setting up DI, because the container, cannot tell from its current registrations, whether Foo actually has all of its dependencies met - because resolution of the dependency is based on a string that isn't provided until an invocation at runtime. So the factory function could return NULL or throw an exception if a named service with the name APOWDJAWOJD hasn't been registered in advance - and the container has no way to check for that.

Enter Typed registrations..

var services = new ServiceCollection();
    services.AddTyped<AnimalService>(types=>
    {
         types.AddSingleton(); // Will resolve any type used as akey 
         types.AddSingleton<BearService, MyKey>(); when resolved using: MyKey as key, will return BearService             
        
    });

Now you can do this:

public class Foo
{
   public Foo(Func<MyKey, AnimalService> typedService)
   {
      var service = typedService(); // BearService
  }
}

public class Bar
{
   public Foo(Func<SomeOtherType, AnimalService> typedService)
   {
      var service = typedService(); // default AnimalService
  }
}

Essentially this would do away with magic strings, and instead rely on "marker types" to resolve different registrations of a service.

The thinking behind this, is that it should allow the container to verify if it has a registrations, and solves basically all of the above problems. What's the downside?

  • You have to choose or create distinct "marker" types to use as keys for registration / resolution.
@dazinator dazinator added the enhancement New feature or request label Mar 9, 2020
@dazinator dazinator added the idea label Sep 22, 2020
@JosXa
Copy link

JosXa commented Nov 18, 2020

I suppose C# 9 positional records could come in handy for declaring the marker types:

public record Duck();
public record Bear();
public record Goose();

image
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#record-types

@dazinator
Copy link
Owner Author

dazinator commented Nov 18, 2020

@JosXa I agree, they look perfect for this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request idea
Projects
None yet
Development

No branches or pull requests

2 participants