Home > Back-end >  How to inject a singleton class into a webapplication builder?
How to inject a singleton class into a webapplication builder?

Time:06-22

I have an interface that I'm using between a UserStore and the database of my users:

public interface IUserDB
{
  // simplified for brevity
  string? GetPasswordHash(string username);
}

And a singleton that implements this (for testing purposes):

public class FakeUserDB : IUserDB
{
  private static FakeUserDB instance;

  private FakeUserDB()
  {
  }

  public static FakeUserDB GetInstance()
  {
    if (instance == null) instance = new FakeUserDB();
    return instance;
  }

  public string? GetPasswordHash(string username)
  {
    return "idk something";
  }
}

Now I have a UserStore that communicates with this singleton, that is passed in via the constructor:

public class MyUserStore : IUserStore<MyUser>, IUserPasswordStore<MyUser>, IUserTwoFactorStore<MyUser>, 
  IUserLockoutStore<MyUser>, IUserRoleStore<MyUser>, IUserClaimStore<MyUser>
{
  private readonly string _connectionString;
  private readonly IUserDB _userDB;

  public MyUserStore(IConfiguration configuration, IUserDB db)
  {
      _connectionString = configuration.GetConnectionString("DefaultConnection");
      _userDB = db;
  }

  // A lot more stuff goes below here
  // ....
}

I'm aware that I need to add this as a dependency injection to my program.cs, but I'm not sure how to do so because FakeUserDB doesn't have a public constructor. When I try:

builder.Services.AddScoped<IUserDB, FakeUserDB>();
builder.Services.AddTransient<IUserStore<MyUser>, MyUserStore>();

It doesn't work. Is there a way that I can add a singleton service to my builder?

CodePudding user response:

Instead of implementing the class as a singleton, you can leave that to the IoC container:

public class FakeUserDB : IUserDB
{
  public FakeUserDB()
  {
  }

  public string? GetPasswordHash(string username)
  {
    return "idk something";
  }
}

In the IoC, you register it as a singleton:

builder.Services.AddSingleton<IUserDB, FakeUserDB>();
builder.Services.AddTransient<IUserStore<MyUser>, MyUserStore>();

As an alternative, if you do not want or cannot change the class, you can register the instance with a factory method like this:

builder.Services.AddSingleton<IUserDB>(prov => FakeUserDB.GetInstance());
builder.Services.AddTransient<IUserStore<MyUser>, MyUserStore>();
  • Related