【问题标题】:Create singleton when private constructor have parameters当私有构造函数有参数时创建单例
【发布时间】:2022-01-18 11:25:00
【问题描述】:

我想在 StudentProvider 中实现单例模式,然后通过接口访问方法。 StudentProvider 构造函数接受的参数很少。这是没有单例的示例工作代码。

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;
    }
}

现在我把它转换成单曲,这里是代码:

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;
    }
}

上述单例代码的问题是instance = new StudentProvider(); 需要我无法传递的参数。

如何将参数从单例实例传递给构造函数?

【问题讨论】:

  • 你不能,基本上。您没有足够的信息 - 除非您可以在该属性中创建所有必需的对象。我建议现在是放弃单例模式(这会损害可测试性并导致全局状态)转而使用依赖注入的好时机。

标签: c# asp.net asp.net-core design-patterns singleton


【解决方案1】:

看来您正在使用 ASP.NET 并且它是依赖注入。如果是这样,您可以使用AddSingleton 来注册您的提供者,而不是实现您自己的单例模式。 Singleton.

顺便说一句,您的提供者依赖于 HttpContext,这意味着您需要为不同的请求创建不同的实例。

【讨论】:

  • 我已经在使用 AddSingleton,但是单页加载会创建 10 个 StudentProvider 对象。
  • 您能否再次验证一下,因为 AddSingleton 只会创建 StudentProvider 的 1 个对象?
  • 我的错,目前是 services.AddTransient();但是一旦我将其更改为 services.AddSingleton();其抛出错误 InvalidOperationException:无法使用单例“Extensions.IStudentProvider”中的范围服务“DataContext”。
  • @XiaofengZheng 让我试试这个,我肯定不会使用 Singleton。我看到我们创建的服务混合使用了 AddScoped 和 AddTransient。此外,将使用 Scoped (之前什么都没有,不确定默认 Lifetime 是什么,如果你知道的话,请告诉它)和 DbContext 一样 services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString(" ConnString")), ServiceLifetime.Scoped);
【解决方案2】:

正如@Jon Skeet 所建议的,使用依赖注入会更好。

我也会向@Xiaofeng Zheng 推荐使用带有工厂模式的单例依赖注入的解决方案。

如果所有这些都不满足,您可以使用以下解决方案。

您需要将 IServiceProvider 的引用保留为可全局访问的启动文件中的单例。

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

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

    ServiceProvider = serviceProvider;
  }
}

然后,您可以访问 StudentProvider 中的 Startup.ServiceProvider 来创建其他依赖项的实例。

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)
    
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-04
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    • 2019-10-16
    相关资源
    最近更新 更多