关于ASP.NET Core中的c#:IServiceProvider

IServiceProvider in ASP.NET Core

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

1
2
3
4
5
6
7
public class Entity
{
     public void DoSomething()
     {
           var dbContext = ServiceContainer.GetService<DataContext>(); //Where is ServiceContainer or something like that ?
     }
}

我知道,我们在启动时配置服务,但是所有服务集合仍保留在哪里还是IServiceProvider?


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

1
GetService< T >();

应在

上使用的扩展方法

1
IServiceProvider

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface ISomeService
{
    string ServiceValue { get; set; }
}

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

    public string ServiceValue { get; set; }
}

Startup.cs

1
2
3
4
5
public void ConfigureService(IServiceCollection services)
{
    ...
    services.AddSingleton<ISomeService, ServiceImplementation>();
}

HomeController

1
2
3
4
5
6
7
8
9
10
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

的其他服务扩展

1
AddInstance<IService>(new Service())

始终提供一个实例。您负责初始对象的创建。

1
AddSingleton<IService, Service>()

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

1
AddTransient<IService, Service>()

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

1
AddScoped<IService, Service>()

在当前HTTP请求范围内创建一个实例。在当前作用域上下文中等效于Singleton。

2018年10月18日更新

请参阅:aspnet GitHub-ServiceCollectionServiceExtensions.cs


使用GetRequiredService代替GetService,例如ASP.NET Core教程(https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/first-mvc-app/working-with-sql )

方法说明:

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

1
2
3
using Microsoft.Extensions.DependencyInjection;

      using (var context = new ApplicationDbContext(serviceProvicer.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
    }
}


请勿使用GetService()

GetService和GetRequiredService之间的差异与异常有关。

如果服务不存在,

GetService()返回null。
GetRequiredService()将引发异常。

1
2
3
4
5
6
7
8
9
10
11
12
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));
    }
}

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


通常,您想让DI来完成它并为您注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
     }
}

但是,必须像ControllerViewComponent一样自动为您实例化Entity。如果您需要在无法使用此dbContext的地方手动实例化它,则可以执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();这是一种非常丑陋的方法,但是有时您只需要使其工作即可。


而不是使您的服务内联,请尝试将其注入到构造函数中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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实例的方式产生重大影响。这是一种称为依赖注入的模式。这需要一段时间才能习惯,但是一旦您习惯了,您将再也不想回去了。