【问题标题】:IServiceProvider in ASP.NET CoreASP.NET Core 中的 IServiceProvider
【发布时间】:2015-07-11 12:09:20
【问题描述】:

我开始学习 ASP.NET 5(vNext) 的变化 并且找不到如何获取 IServiceProvider,例如在“模型”的方法中

public class Entity 
{
     public void DoSomething()
     { 
           var dbContext = ServiceContainer.GetService<DataContext>(); //Where is ServiceContainer or something like that ?
     }
}

我知道,我们在启动时配置服务,但是所有服务集合或 IServiceProvider 都放在哪里?

【问题讨论】:

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


    【解决方案1】:

    您必须引入 Microsoft.Extensions.DependencyInjection 命名空间才能访问泛型

    GetService<T>();
    

    应该使用的扩展方法

    IServiceProvider 
    

    另请注意,您可以直接将服务注入 ASP.NET 5 中的控制器。请参见下面的示例。

    public interface ISomeService
    {
        string ServiceValue { get; set; }
    }
    
    public class ServiceImplementation : ISomeService
    {
        public ServiceImplementation()
        {
            ServiceValue = "Injected from Startup";
        }
    
        public string ServiceValue { get; set; }
    }
    

    Startup.cs

    public void ConfigureService(IServiceCollection services)
    {
        ...
        services.AddSingleton<ISomeService, ServiceImplementation>();
    }
    

    家庭控制器

    using Microsoft.Extensions.DependencyInjection;
    ...
    public IServiceProvider Provider { get; set; }
    public ISomeService InjectedService { get; set; }
    
    public HomeController(IServiceProvider provider, ISomeService injectedService)
    {
        Provider = provider;
        InjectedService = Provider.GetService<ISomeService>();
    }
    

    任何一种方法都可用于访问服务。 Startup.cs 的其他服务扩展

    AddInstance<IService>(new Service())
    

    始终给出一个实例。您负责创建初始对象。

    AddSingleton<IService, Service>()
    

    创建了一个实例,它的行为就像一个单例。

    AddTransient<IService, Service>()
    

    每次注入时都会创建一个新实例。

    AddScoped<IService, Service>()
    

    在当前 HTTP 请求范围内创建单个实例。它相当于当前作用域上下文中的 Singleton。

    2018 年 10 月 18 日更新

    见:aspnet GitHub - ServiceCollectionServiceExtensions.cs

    【讨论】:

    • 这似乎在ASP.NET 5 中不起作用,这就是OP 的意义所在。也许我错过了什么?
    • 自从这篇文章以来它已经改变了。如果我没记错的话,这是从 Beta 7 开始的?这绝对是参考 ASP.NET 5。他们将命名空间从 Microsoft.Framework.DependencyInjection 重命名为 Microsoft.Extensions.DependencyInjection。下面是定义 GetService 扩展的地方:github.com/aspnet/DependencyInjection/blob/dev/src/…
    • 嗯...刚试过:DNX 4.6 using Microsoft.Extensions.DependencyInjection;GetService&lt;IHostingEnvironment&gt;();导致错误,The name 'GetService' does not exist in the current context.
    • 将 IServiceProvider 传递给作为容器引用的控制器而不是传递依赖项本身不是一种不好的做法吗?
    • @SimpleFellow - 我在想同一行。通过传递 IServiceProvider,模式将只是使用 DI“传递定位器对象”。但是在用这种模式编码一段时间后,我开始认为传递 IServiceProvider 的实用性值得重新考虑,对我来说,至少在工作服务上(可能不在 rest apis 中)。
    【解决方案2】:

    我认为实体(或模型)访问任何服务都不是一个好主意。

    另一方面,控制器确实可以访问其构造函数中的任何注册服务,您不必担心。

    public class NotifyController : Controller
    {
        private static IEmailSender emailSender = null;
        protected static ISessionService session = null;
        protected static IMyContext dbContext = null;
        protected static IHostingEnvironment hostingEnvironment = null;
    
        public NotifyController(
                    IEmailSender mailSenderService,
                    IMyContext context,
                    IHostingEnvironment env,
                    ISessionService sessionContext)
        {
            emailSender = mailSenderService;
            dbContext = context;
            hostingEnvironment = env;
            session = sessionContext;
        }
    }
    

    【讨论】:

    • 现在我也觉得:)
    【解决方案3】:

    使用 GetRequiredService 而不是 GetService,例如 ASP.NET Core 教程 (https://docs.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/working-with-sql) 上的示例

    方法文档:

    https://docs.microsoft.com/en-us/aspnet/core/api/microsoft.extensions.dependencyinjection.serviceproviderserviceextensions#Microsoft_Extensions_DependencyInjection_ServiceProviderServiceExtensions_GetRequiredService__1_System_IServiceProvider_

    using Microsoft.Extensions.DependencyInjection;
    
          using (var context = new ApplicationDbContext(serviceProvicer.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
    

    【讨论】:

      【解决方案4】:

      我认为 OP 变得很困惑。实体应尽可能“薄”。它们应尽量不包含逻辑和/或导航属性以外的外部引用。查找一些常见模式,例如存储库模式,这有助于将您的逻辑从实体本身中抽象出来

      【讨论】:

        【解决方案5】:

        不要使用GetService()

        GetService 和 GetRequiredService 的区别与异常有关。

        如果服务不存在,GetService() 将返回 null。 GetRequiredService() 会抛出异常。

        public static class ServiceProviderServiceExtensions
        {
            public static T GetService<T>(this IServiceProvider provider)
            {
                return (T)provider.GetService(typeof(T));
            }
        
            public static T GetRequiredService<T>(this IServiceProvider provider)
            {
                return (T)provider.GetRequiredService(typeof(T));
            }
        }
        

        【讨论】:

          【解决方案6】:

          通常你想让 DI 做它的事情并为你注入:

          public class Entity 
          {
              private readonly IDataContext dbContext;
          
              // The DI will auto inject this for you
              public class Entity(IDataContext dbContext)
              {
                  this.dbContext = dbContext;
              }
          
               public void DoSomething()
               {
                   // dbContext is already populated for you
                   var something = dbContext.Somethings.First();
               }
          }
          

          但是,Entity 必须自动为您实例化...例如 ControllerViewComponent。如果您需要在此 dbContext 对您不可用的地方手动实例化它,那么您可以这样做:

          using Microsoft.Extensions.PlatformAbstractions;
          
          public class Entity 
          {
              private readonly IDataContext dbContext;
          
              public class Entity()
              {
                  this.dbContext = (IDataContext)CallContextServiceLocator.Locator.ServiceProvider
                                      .GetService(typeof(IDataContext));
              }
          
               public void DoSomething()
               {
                   var something = dbContext.Somethings.First();
               }
          }
          

          但要强调的是,这被认为是一种反模式,除非绝对必要,否则应避免使用。并且...冒着让某些模式让人非常沮丧的风险...如果所有其他方法都失败了,您可以在帮助类或其他东西中添加一个static IContainer,然后在ConfigureServices 方法中的StartUp 类中分配它:MyHelper.DIContainer = builder.Build(); 这是一个非常丑陋的方法,但有时你只需要让它工作。

          【讨论】:

          • 这是“不做依赖注入有多少种方法”的完美示例
          【解决方案7】:

          不要让你的服务内联,而是尝试将它注入到构造函数中。

          public class Startup
          {
              public void ConfigureServices(IServiceCollection services)
              {
                  services.AddTransient(typeof(DataContext));
              }
          }
          
          public class Entity
          {
              private DataContext _context;
          
              public Entity(DataContext context)
              {
                  _context = context;
              }
          
              public void DoSomething()
              {
                  // use _context here
              }
          }
          

          我还建议阅读AddTransient 的含义,因为它将对您的应用程序如何共享 DbContext 实例产生重大影响。这是一种称为Dependency Injection 的模式。习惯需要一段时间,但一旦习惯了就再也不想回去了。

          【讨论】:

          • 感谢您的回答,但是如果我有很多实体,我需要重写构造函数,并且不能使用 Activator.CreateInstance,在方法参数中使用注入看起来很难看,你能告诉我下面的 YAGNI 的好模式吗?
          • 您是否尝试使用实体框架?注入用于构造函数,而不是方法。我不确定你在这种情况下对 YAGNI 的意思。
          • 是的,我正在使用实体框架,但看看这个案例:我有实体,例如 - 目录,并且目录有属性 - Url,在旧 MVC 中我可以创建 httpcontext 包装器并使用属性内的 url 助手,在新的 MVC 中我不能这样做,并且除了在 DRY 的方法中包含 IUrlHelper(在属性路径上添加新的方法包装器)之外没有变体,但这仍然很奇怪
          • 我相信@Sam 所引用的练习来自以下内容:docs.asp.net/projects/mvc/en/latest/tutorials/… 在本练习中,他们明确使用了 Microsoft.Framework.DependencyInjection 中定义的通用 GetService 扩展方法。
          • 构造函数注入没问题,但是EF怎么样,他们在从数据库实现的同时注入对象吗? @安东
          猜你喜欢
          • 2019-01-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多