Parth Sekar
Parth Sekar

Reputation: 418

Pass an object to multiple IServiceCollection Extensions

I need to pass an object to several IServiceCollection extensions that I have created and all my extensions methods uses that object and registers their concerned dependencies. The below is how my code looks like:

Program.cs

var myObject = new MyObject(); //MyObject implements IMyObject
var builder = WebApplication.CreateBuilder(args);
builder.Services
       .AddMyExtension1(myObject)
       .AddMyExtension2(myObject)
       .AddMyExtension3(myObject)
       ...
       .AddMyExtensionN(myObject);

var app = builder.Build();
...
app.Run();

Sample Extension:

public static class MyExtensions{

  public static IServiceCollection AddMyExtension1(
        this IServiceCollection services,
        IMyObject myObject)
  {
     //do something
     //register some dependencies
     return services;
  }
}

I understand that using BuildServiceProvider can cause code smell/ "results in an additional copy of singleton services being created." Can anyone please recommend a better approach to use myObject instance in my extension methods rather than passing it as a parameter in all my method?

Upvotes: 0

Views: 83

Answers (1)

Guru Stron
Guru Stron

Reputation: 142008

Without seeing the actual usage of myObject it is hard to argue about what is the best approach. If it is required for all the custom AddMyExtension.. methods possibly it should be just injected and resolved at runtime for corresponding registrations.

If it is not possible or just bad idea in your case you can create a new interface with implementation being basically wrapper for IServiceCollection and use it for registrations. Something like this:

public interface IMyServiceCollection : IServiceCollection
{
    public IMyObject MyObject { get; }
}

class MyServiceCollection : IMyServiceCollection
{
    private readonly IServiceCollection _serviceCollection;

    public IMyObject MyObject { get; }

    public MyServiceCollection(IServiceCollection serviceCollection, IMyObject myObject)
    {
        _serviceCollection = serviceCollection;
        MyObject = myObject;
    }

    public IEnumerator<ServiceDescriptor> GetEnumerator() => _serviceCollection.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_serviceCollection).GetEnumerator();

    public void Add(ServiceDescriptor item) => _serviceCollection.Add(item);

    public void Clear() => _serviceCollection.Clear();

    public bool Contains(ServiceDescriptor item) => _serviceCollection.Contains(item);

    public void CopyTo(ServiceDescriptor[] array, int arrayIndex) => _serviceCollection.CopyTo(array, arrayIndex);

    // the rest of IServiceCollection delegating implementation ...
}

Extensions:

public static class ServicesExts
{  
    public static IMyServiceCollection WithMyObject(this IServiceCollection services, IMyObject obj) => new MyServiceCollection(services, obj);
}

And then make all the AddMyExtension... methods accept and return IMyServiceCollection with the calls changing to:

builder.Services
    .WithMyObject(myObject)
    .AddMyExtension1()
    .AddMyExtension2()
    ...
    .AddMyExtensionN();

Or similar approach with value tuples:

public static class ServicesExts
{  
    public static (IServiceCollection Collection, IMyObject Object) WithMyObject(this IServiceCollection services, IMyObject obj) 
        => (services, obj);
}

And AddMyExtension... loking somewhat like:

public static (IServiceCollection Collection, object Object) AddMyExtension(this (IServiceCollection Collection, object Object) tuple)
{
     // do the registrations ...
     return tuple;
}

With the usage looking the same.

Upvotes: 1

Related Questions