Home > OS >  Create singleton when private constructor have parameters
Create singleton when private constructor have parameters

Time:12-15

I would like to implement singleton pattern in StudentProvider and then access method through interface. StudentProvider constructor accepts few parameters. Here's the sample working code without singleton.

public interface IStudentProvider
{
    Task<StudentViewModel> GetStudentAsync();
}

public class StudentProvider : IStudentProvider
{
    private readonly HttpContext httpContext;
    private readonly IActionContextAccessor actionContextAccessor;
    private readonly IConfiguration configuration;
    private readonly IUnitOfWork unitOfWork;
    private readonly string host;

    public StudentProvider(IHttpContextAccessor _httpContextAccessor, IActionContextAccessor _actionContextAccessor, IConfiguration _configuration, IUnitOfWork _unitOfWork)
    {
        httpContext = _httpContextAccessor.HttpContext;
        
        actionContextAccessor = _actionContextAccessor;
        configuration = _configuration;
        unitOfWork = _unitOfWork;
        host = _httpContextAccessor.HttpContext.Request.Host.Host;
    }

    public async Task<StudentViewModel> GetStudentAsync()
    {
        var std = new StudentViewModel();

        // httpContext, actionContextAccessor, configuration, unitOfWork and host uses here

        return std;
    }
}

Now i converted this into single, here's the code:

public interface IStudentProvider
{
    Task<StudentViewModel> GetStudentAsync();
}

public sealed class StudentProvider : IStudentProvider
{
    private readonly HttpContext httpContext;
    private readonly IActionContextAccessor actionContextAccessor;
    private readonly IConfiguration configuration;
    private readonly IUnitOfWork unitOfWork;
    private readonly string host;

    private static StudentProvider instance = null;

    public static StudentProvider GetInstance
    {
        get
        {
            if (instance == null)
            {
                instance = new StudentProvider();
            }

            return instance;
        }
    }

    private StudentProvider(IHttpContextAccessor _httpContextAccessor, IActionContextAccessor _actionContextAccessor, IConfiguration _configuration, IUnitOfWork _unitOfWork)
    {
        httpContext = _httpContextAccessor.HttpContext;

        actionContextAccessor = _actionContextAccessor;
        configuration = _configuration;
        unitOfWork = _unitOfWork;
        host = _httpContextAccessor.HttpContext.Request.Host.Host;
    }

    public async Task<StudentViewModel> GetStudentAsync()
    {
        var std = new StudentViewModel();

        // httpContext, actionContextAccessor, configuration, unitOfWork and host uses here

        return std;
    }
}

The issue with above singleton code is instance = new StudentProvider(); is expecting parameters which i'm not able to pass.

How do i pass parameters to constructor from singleton instance ?

CodePudding user response:

It seems that you're using ASP.NET and it's dependency injection. If so, you can use AddSingleton to register your provider instead of implementing your own singleton pattern. Singleton.

BTW, your provider depends on a HttpContext which means you need to create different instance for different requests.

CodePudding user response:

As @Jon Skeet suggested, it will be better to use Dependency Injection.

I will also recommend to @Xiaofeng Zheng solution to use the singleton dependency injection with factory pattern.

And if all these does not satisfy, you can go with below solution.

You will need to keep the reference of IServiceProvider as singleton in your Startup file which can be accessed globally.

public class Startup
{
  public static IServiceProvider ServiceProvider { get; private set; }

  public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider) {
    ...

    ServiceProvider = serviceProvider;
  }
}

Then, you can access the Startup.ServiceProvider within your StudentProvider to create the instance of other dependencies.

using Microsoft.Extensions.DependencyInjection;

public sealed class StudentProvider : IStudentProvider
{
    private readonly HttpContext httpContext;
    private readonly IActionContextAccessor actionContextAccessor;
    private readonly IConfiguration configuration;
    private readonly IUnitOfWork unitOfWork;
    private readonly string host;

    private static StudentProvider instance = null;

    public static StudentProvider GetInstance
    {
        get
        {
            if (instance == null)
            {
                instance = new StudentProvider(
                    Startup.ServiceProvider.GetService<IHttpContextAccessor>(), 
                    Startup.ServiceProvider.GetService<IActionContextAccessor>(), 
                    Startup.ServiceProvider.GetService<IConfiguration>(), 
                    Startup.ServiceProvider.GetService<IUnitOfWork>()
                );
            }

            return instance;
        }
    }

    private StudentProvider(IHttpContextAccessor _httpContextAccessor, IActionContextAccessor _actionContextAccessor, IConfiguration _configuration, IUnitOfWork _unitOfWork)
    
}
  • Related