【问题标题】:Working with dependency injection in mvc在 mvc 中使用依赖注入
【发布时间】:2016-08-26 13:07:52
【问题描述】:

在我们的一个应用程序中,我已经在使用 AppTenant 类的依赖注入,如下所示

public void ConfigureServices(IServiceCollection services)
{

services.AddMultitenancy<AppTenant, CachingAppTenantResolver>();
services.Configure<MultitenancyOptions>(Configuration.GetSection("Multitenancy"));
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
 app.UseMultitenancy<AppTenant>();
}

在控制器中,我可以按如下方式轻松访问它

public AccountController(AppTenant tenant)
    {
        this.tenant = tenant;
    }

现在,我想在同一解决方案的其他项目类中访问相同的 AppTenantHttpContext。 所以,我试过这样

public SqlStringLocalizerFactory(
       AppTenant tenant)
    {
     _tenant = tenant;

    }

但它即将为空,所以我需要做什么才能在另一个项目类中获得 AppTenantHttpContext

对于SqlStringLocalizerFactory 类,服务是用ConfigureServices 方法编写的,如下所示

public static class SqlLocalizationServiceCollectionExtensions
{

    public static IServiceCollection AddSqlLocalization(this IServiceCollection services)
    {
        if (services == null)
        {
            throw new ArgumentNullException(nameof(services));
        }

        return AddSqlLocalization(services, setupAction: null);
    }

   public static IServiceCollection AddSqlLocalization(
        this IServiceCollection services,
       Action<SqlLocalizationOptions> setupAction)
    {

            if (services == null)
        {
            throw new ArgumentNullException(nameof(services));
        }

        services.TryAdd(new ServiceDescriptor(
            typeof(IStringExtendedLocalizerFactory),
            typeof(SqlStringLocalizerFactory),
            ServiceLifetime.Singleton));
        services.TryAdd(new ServiceDescriptor(
            typeof(IStringLocalizerFactory),
            typeof(SqlStringLocalizerFactory),
            ServiceLifetime.Singleton));
        services.TryAdd(new ServiceDescriptor(
            typeof(IStringLocalizer),
            typeof(SqlStringLocalizer),
            ServiceLifetime.Singleton));

        if (setupAction != null)
        {
            services.Configure(setupAction);
        }
        return services;
    }
}

我什至尝试过IHttpContextAccessor,但仍然没有成功。

对此的任何帮助表示赞赏!

【问题讨论】:

  • 您是否将IHttpContextAccessor 配置/添加到服务中。它不会自动添加。您必须手动添加它才能让服务知道它。即services.AddSingleton&lt;IHttpContextAccessor, HttpContextAccessor&gt;();
  • 您已将SqlStringLocalizerFactory 注册为singleton,因此在您的应用程序的整个生命周期中只有一个。单例生活方式与HttpContext 的生命周期不匹配,HttpContext 的生命周期是每个请求,并留下一个强制依赖
  • @Nkosi 是的,我已经添加并在课堂上写为private IHttpContextAccessor _contextAccessor public SqlStringLocalizerFactory( IHttpContextAccessor contextAccessor)
  • @qujck 那么我需要如何注册SqlStringLocalizerFactory class 呢?与AddScopedAddTransient ?
  • @Rohit 抱歉,我帮不了你,我没有投资 .NET Core DI

标签: c# dependency-injection asp.net-core asp.net-core-mvc asp.net-core-1.0


【解决方案1】:

Edit-2

新解决方案:

public SqlStringLocalizerFactory(IHttpContextAccessor _accessor)
{
   _accessor= accessor;
}
public void SomeMethod()
{
    var tenant = _accessor.HttpContext.RequestServices
            .GetRequiredService<AppTenant>();
}

编辑IServiceProvider 方式不像我预期的那样工作。查看@Sock 的解决方案

首先,我假设问题的发生是因为@qujck 指出的强制依赖。避免强制依赖:

如果SqlStringLocalizerFactory 的生命周期必须是单例的(某些情况下必须是),在这个 案例使用IServiceProvider:

public SqlStringLocalizerFactory(IServiceProvider serviceProvider)
{
   _serviceProvider = serviceProvider;
}
public void SomeMethod()
{
    var tenant =  _serviceProvider.GetService<AppTenant>();
}

否则使用AddScoped 对您的情况来说似乎是合理的。

【讨论】:

  • 在这种情况下,如果 SqlStringLocalizerFactory 被注册为单例,则从 IServiceProvider 解析的 AppTenant 也将根据此 Twitter 交换在单例范围内:twitter.com/julielerman/status/761179861935394817跨度>
  • 我假设SomeMethod 将在请求范围内被调用,因为它需要AppTenant(不在ConfigureServices 等范围内)。在这种情况下,我认为IServiceProvider 可以正确解析AppTenant。我错了吗?
  • 事件虽然您在请求的上下文中调用SomeMethod,但如果SqlStringLocalizerFactory 注册为单例,则注入的IServiceProvider 将创建单例范围的对象。因此,您对_serviceProvider.GetService&lt;AppTenant&gt;(); 的每次调用都将返回相同的AppTenant 实例,即使AppTenant 已注册为Scoped。为了使AppTenant 具有作用域生命周期,您需要使用ServiceScopeFactory,如下所示。
  • 如果您使用这种方法(编辑 2),请注意您必须使用 services.AddSingleton&lt;IHttpContextAccessor, HttpContextAccessor&gt;(); 在您的服务中显式注册 IHttpContextAccessor。由于“非平凡的性能成本”,它不再默认注册。见github.com/aspnet/Hosting/issues/793
  • 您是否像之前 Sock 的评论中指出的那样明确注册了 IHttpContextAccessor?试试services.AddSingleton&lt;IHttpContextAccessor, HttpContextAccessor&gt;();
【解决方案2】:

您有两个选择,最好的选择是,如果 SqlStringLocalizerFactory 可以是一个作用域依赖项(您为每个请求获取一个新实例),那么您可以将其注册为一个作用域依赖项:

services.TryAdd(new ServiceDescriptor(
        typeof(IStringLocalizerFactory),
        typeof(SqlStringLocalizerFactory),
        ServiceLifetime.Scoped));

如果SqlStringLocalizerFactory 必须是一个单例依赖,那么你需要确保你使用ServiceSope解决租户的范围依赖:

public class SqlStringLocalizerFactory
{
    private readonly IServiceProvider _serviceProvider;

    public SqlStringLocalizerFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void SomeMethod()
    {
        using (var serviceScope = _serviceProvider
            .GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            var tenant = serviceScope.ServiceProvider.GetService<AppTenant>();

            // do something with tenant...
        }
    }
}

【讨论】:

  • 我测试了您的解决方案,如果 AppTenant 在调用 SomeMethod 之前已解决,则会为同一请求创建两个不同的 AppTenant。这可能是个问题。
  • 这是真的,这取决于我认为 AppTenant 本身是如何解决的。在 SaasKit 中,每个请求解析一次租户并存储在 HttpContext.Items 属性中。通过 DI 注入时,将返回来自 HttpContextAppTenant。在这种情况下,多次解析它仍然会返回相同的对象。但是,如果每次注入新对象时都会解析它,那么您是正确的。
  • @Sock 感谢您的回复。我已经尝试过您的解决方案,但“IServiceProvider”即将到来NULL
  • 我认为您使用SqlStringLocalizerFactory 的位置可能有问题。我测试了这个解决方案,将其注入控制器并按预期工作。我注意到您还在上面添加了一条评论,说您也无法解析 IHttpContextAccessor - 这向我表明您没有在请求的上下文中使用 SqlStringLocalizerFactory。是这样吗?
猜你喜欢
  • 1970-01-01
  • 2014-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-14
  • 2021-11-07
  • 1970-01-01
相关资源
最近更新 更多