SaucyRegistrations 1.0.0

dotnet add package SaucyRegistrations --version 1.0.0
NuGet\Install-Package SaucyRegistrations -Version 1.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SaucyRegistrations" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SaucyRegistrations --version 1.0.0
#r "nuget: SaucyRegistrations, 1.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SaucyRegistrations as a Cake Addin
#addin nuget:?package=SaucyRegistrations&version=1.0.0

// Install SaucyRegistrations as a Cake Tool
#tool nuget:?package=SaucyRegistrations&version=1.0.0

Outstanding features / issues

Issue Link
Support for keyed services https://github.com/aspeckt-112/SaucyRegistrations/issues/23
Allow excluding certain classes in a namespace https://github.com/aspeckt-112/SaucyRegistrations/issues/24
Support for generic classes https://github.com/aspeckt-112/SaucyRegistrations/issues/33

Saucy Registrations

I've worked on a few large enterprise projects that made use of a dependency injection container. On multiple different projects, at multiple different jobs, there existed some code to "automagically" register services with the container following some sort of convention. For example, classes that ended with "Repository" would be registered as a singleton, while classes that ended with "Service" would be registered as transient, etc. This code was always done with reflection, normally a pain to maintain and debug and always seemed to be a bit of a black box.

I wanted to solve the problem for myself, once and for all. I wanted to do it in a way that was quick and easy to understand.

Enter...this.

What is this?

Saucy is an incremental source generator. It provides a few attributes and an enum. Using these, it'll then generate a class within the same assembly that contains all the registrations for your services. You simply call the extension method it generates in your composition root and forget about it.

How do I use this?

See the samples for a working example. Read on if you want to know more, or you're just procrastinating at work, trying to look productive by reading documentation on GitHub.

At it's core, usage is as simple as adding a single attribute to your service class. For example:

[SaucyInclude(ServiceScope.Singleton)]
public class ExampleClass : IExample
{
}

Which is turn, generates the following code:

namespace YOUR_PROJECT.ServiceCollectionExtensions;

public static class YOUR_PROJECTServiceCollectionExtensions
{
    public static void AddYOUR_PROJECTServices(IServiceCollection services)
    {
        services.AddSingleton<IExample, ExampleClass>();
    }
}

You can then call this method in your composition root:

var serviceCollection = new ServiceCollection();
serviceCollection.AddYOUR_PROJECTServices();

In essence, that's it.

But this isn't saving me much time?

At this point you may be thinking "This isn't saving me much time, I could have just written the registration code myself". And you'd be right. But the real power of Saucy comes when you have a large project with many services, in many different namespaces. To help you with this, Saucy provides another, more powerful attribute: SaucyIncludeNamespaceWithSuffix.

SaucyIncludeNamespaceWithSuffix is an attribute that you apply at the assembly level. It tells Saucy to always register classes in that namespace with a specific suffix. For example:

AssemblyInfo.cs
[assembly: SaucyIncludeNamespaceWithSuffix(nameof(Saucy.Services), ServiceScope.Transient)]

With this, all classes in the Saucy.Services namespace will be registered as transient. This is particularly useful when you have a large project with many services, and you want to register them all in the same way.

There's no limitation on the amount of attributes you can apply to an assembly, so you can have multiple namespaces with different scopes.

[assembly: SaucyIncludeNamespaceWithSuffix(nameof(Saucy.Services), ServiceScope.Transient)]
[assembly: SaucyIncludeNamespaceWithSuffix(nameof(Saucy.Builders), ServiceScope.Singleton)]

Which would generate the following code:

// <auto-generated by Saucy. DO NOT CHANGE THIS FILE!!! />
using Microsoft.Extensions.DependencyInjection;

namespace Saucy.Console.ServiceCollectionExtensions;

public static class SaucyConsoleServiceCollectionExtensions
{
    public static IServiceCollection AddSaucyConsoleServices(this IServiceCollection services)
    {
        services.AddSingleton<Saucy.Console.Builders.IBuilderInterfaceOne, Saucy.Console.Builders.ABuilder>();
        services.AddTransient<Saucy.Console.Services.IService, Saucy.Console.Services.IncludedServiceOne>();
        return services;
    }
}

Note: services are registered alphabetically by their fully qualified name. A purely arbitrary choice that I made.

If you need to override the default scope for a specific class, you can do so by applying the SaucyInclude attribute to the class itself. This will override the scope set by the namespace attribute.

Anything else?

Yes! By default, abstract classes won't be registered. I'm open to feedback on this, but from experience, I can count on one hand that I've actually resolved an abstract class from the container. If you need to register an abstract class. However, if you need, you can do so by applying the SaucyRegisterAbstractClass attribute to the class.

[SaucyInclude(ServiceScope.Transient)]
[SaucyRegisterAbstractClass]
public class SomethingUsingTheAbstractBaseClassAndTheInterface : AbstractRegistrationBaseClass, ISomeInterface
{

}

If you've got a class that implements multiple interfaces, but you only want to register it as one of them, you can apply one (or more) SaucyDoNotRegisterWithInterface attribute(s) to the class.

[SaucyInclude(ServiceScope.Scoped)]
[SaucyDoNotRegisterWithInterface(nameof(IService))]
public class IncludedServiceWithScopeOverride : IService
{

}

Doing that will register a concrete instance of IncludedServiceWithScopeOverride with the container, but it won't register it as an IService.

Support for generics?

Saucy currently supports generics at an interface level, but not at a class level.

There's an open issue here: https://github.com/aspeckt-112/SaucyRegistrations/issues/33

I'm open to feedback, ideas and pull requests. Enjoy using Saucy!
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.0 84 5/16/2024
0.0.7-beta 298 4/16/2024
0.0.6-beta 77 4/16/2024
0.0.5-beta 90 4/14/2024
0.0.3-beta 90 4/6/2024
0.0.2-beta 72 3/24/2024
0.0.1-beta 73 3/24/2024