Bindicate

As .NET developers, we’ve all been there — adding a new service to an application and realizing that we need to jump through hoops to wire it up in the IoC container. This often involves navigating through layers of code, digging through Startup.cs or equivalent files, and explicitly adding the new service. Wouldn’t it be great if there was a way to streamline this? Imagine a flag or marker that simply tells the framework, “Hey, I’m here, and I need to be registered with this scope.”

Well, that’s precisely what Bindicate aims to achieve.


What is Bindicate?

Bindicate is a simple NuGet package designed to declutter your .NET configuration by enabling attribute-based service registration. By using attributes, you can dictate how your classes should be registered in the IoC container. It currently supports Scopes, Transients, and Singletons, both in the normal and “TryAdd” variants, and the keyed variants for .NET 8 and onwards.

Clean Code

You no longer have to comb through a mountain of services.Add...<,>() lines. Bindicate allows you to specify the registration logic at the service class level.

Better maintainability

When the registration is closely associated with the service class, it’s easier to change the scope of the service without hunting down the registration code in a different part of the project.

Getting started

Installation

To get started, install the package from NuGet:

Install-Package Bindicate

Usage

Decorating Services

Once installed, you can start decorating your service classes with Bindicate attributes. For example, to register a service as a Scoped dependency, you can do the following:

[AddScoped]
public class MyScopedService
{
  // Implementation goes here
}

//Or

[AddTransient(typeof(IMyService))]
public class MyService: IMyService
{
    void DoThing() {}
}
public interface IMyService 
{
    void DoThing();
}

Bootstrapping

After your services are appropriately decorated, you need to initialize Bindicate in your Startup.cs (or program.cs in .NET6):

// .NET6 in program.cs
builder.Services
  .AddAutopwiring(Assembly.GetExecutingAssembly())
  .Register();

// Startup.cs in earlier versions
public void ConfigureServices(IServiceCollection services)
{
    services.AddAutowiring(Assembly.GetExecutingAssembly())
        .Register();
    // Other configurations
}

Don’t forget to call .Register()!

And that’s it! Bindicate will now register your services based on the attributes you’ve specified.

Keyed Services (.NET 8)

With keyed services, another piece of information is stored with the ServiceDescriptor, a ServiceKey that identifies the service. The key can be any object, but it will commonly be a string or an enum (something that can be a constant so it can be used in attributes). For non-keyed services, the ServiceType identifies the registration; for keyed services, the combination of ServiceType and ServiceKey identifies the registration.

To register a keyed service, you van use the AddKeyed[Lifetime] attribute and pass the key as parameters

[AddKeyedScoped("myKey")]
public class KeyedService
{
 public void Run()
 {
  // ...
 }
}

[AddKeyedScoped("key", typeof(IKeyedService))]
public class KeyedService : IKeyedService
{
 public void Run()
 {
  // ...
 }
}

[AddKeyedScoped("anotherKey", typeof(IKeyedService))]
public class AnotherKeyedService : IKeyedService
{
 public void Run()
 {
  // ...
 }
}

Bootstrapping keyed services

Add the ForKeyedServices() method call to your configuration

// program.cs
builder.Services
   .AddAutopwiring(Assembly.GetExecutingAssembly())
   .ForKeyedServices()
   .Register();

Registering options

In addition, you can register IOptions to use pass options to your services.

[RegisterOptions("testOptions")]
public class TestOptions
{
    public string Test { get; set; } = "";
}

//appsettings.json:
{
  "testOptions": {
    "test": "test"
  }
}

Then update your ServiceCollectionExtensions to autowire your options:

builder.Services
    .AddAutowiringForAssembly(Assembly.GetExecutingAssembly())
    .WithOptions(Configuration)  //Pass builder.Configuration here
    .Register();

Limitations

Single Attribute Policy: Bindicate currently supports only one attribute per class to prevent ambiguous registrations.

Conclusion

Bindicate makes dependency injection in .NET applications clean and straightforward. By shifting the responsibility of registering services closer to the service classes themselves, it promotes cleaner and more maintainable code. So why not give it a try and simplify your DI configuration?

Find Bindicate on NuGet and on GitHub.


<
Previous Post
Hello GitHub Pages
>
Next Post
Comparing .NET Dependency Injection Libraries: Bindicate, Ninject, Autofac, and Scrutor