使用Controller类以外的类的依赖注入

时间:2016-05-12 14:30:06

标签: c# asp.net-core asp.net-core-mvc .net-core

此时我正在轻松地将内容注入我的控制器中,在某些情况下构建我自己的ResolverServices类。 生活是美好的

我无法弄清楚如何做到让框架自动注入非控制器类。框架自动注入我的控制器IOptions的工作是什么,这实际上是我项目的配置:

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;

    public MessageCenterController(IOptions<MyOptions> options)
    {
        _options = options.Value;
    }
}

我认为我可以为自己的班级做同样的事情,我认为当我模仿控制器时,我很接近,就像这样:

public class MyHelper
{
    private readonly ProfileOptions _options;

    public MyHelper(IOptions<ProfileOptions> options)
    {
        _options = options.Value;
    }

    public bool CheckIt()
    {
        return _options.SomeBoolValue;
    }
}

我认为我失败的地方就是我这样称呼它:

public void DoSomething()
{
    var helper = new MyHelper(??????);

    if (helper.CheckIt())
    {
        // Do Something
    }
}

我跟踪此问题的几乎所有关于DI的内容都是在控制器级别讨论它。我尝试在Controller对象源代码中搜索它发生的位置,但它在那里变得有点疯狂。

我知道我可以手动创建IOptions实例并将其传递给MyHelper构造函数,但似乎我应该能够让框架执行此操作,因为它适用于Controllers

感谢您的帮助。

5 个答案:

答案 0 :(得分:22)

以下是使用DI而没有涉及MVC控制器的任何内容的工作示例。这是我理解这个过程所需要做的事情,所以也许它会帮助别人。

ShoppingCart对象通过DI获取一个INotifier实例(通知客户他们的订单。)

atexit.register

答案 1 :(得分:18)

我们假设MyHelperMyService使用,而MyService则由您的控制人使用。

解决这种情况的方法是:

    {li>

    Register MyHelper中的Startup.ConfigureServicesservices.AddTransient<MyService>(); services.AddTransient<MyHelper>();

    MyService
  • 控制器在其构造函数中接收public HomeController(MyService service) { ... } 的实例。

    MyService
  • MyHelper构造函数将依次接收public MyService(MyHelper helper) { ... } 的实例。

    IServiceProvider

DI框架将能够毫无问题地解析整个对象图。如果您担心每次解析对象时都会创建新实例,那么您可以阅读不同的lifetime and registration options,例如单身或请求生命周期。

当您认为必须手动创建某项服务的实例时,您应该非常怀疑,因为您最终可能会进入service locator anti-pattern。最好将对象创建到DI容器。如果您确实发现自己处于这种情况(假设您创建了一个抽象工厂),那么您可以直接使用IServiceProvider(在构造函数中请求var foo = serviceProvider.GetRequiredService<MyHelper>(); 或使用{{1}} { {3}})。

{{1}}

我建议阅读有关ASP.Net 5 DI框架的特定exposed in the httpContext以及一般的依赖注入。

答案 2 :(得分:5)

不幸的是没有直接的方法。我设法使其工作的唯一方法是创建一个静态类并在其他地方使用它,如下所示:

public static class SiteUtils
{

 public static string AppName { get; set; }

    public static string strConnection { get; set; }

}

然后在你的启动课程中,填写如下:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //normal as detauls , removed for space 
    // set my variables all over the site

    SiteUtils.strConnection = Configuration.GetConnectionString("DefaultConnection");
    SiteUtils.AppName = Configuration.GetValue<string>("AppName");
}

虽然这是一个糟糕的模式,因为这将保留应用程序的整个生命周期,我找不到更好的方法在控制器外使用它。

答案 3 :(得分:2)

基于当前的.NET Core 2.2 DI文档here,这是一个更完整的示例,可以直接回答OP的问题。添加此答案,因为它可能会对.NET Core DI的新手有所帮助,并且因为该问题是Google的顶级搜索结果。

首先,为MyHelper添加一个接口:

public interface IMyHelper
{
    bool CheckIt();
}

第二,更新MyHelper类以实现该接口(在Visual Studio中,按ctrl-。以实现该接口):

public class MyHelper : IMyHelper
{
    private readonly ProfileOptions _options;

    public MyHelper(IOptions<ProfileOptions> options)
    {
        _options = options.Value;
    {

    public bool CheckIt()
    {
        return _options.SomeBoolValue;
    }
}

第三,在DI服务容器中将接口注册为框架提供的服务。为此,请在Startup.cs的ConfigureServices方法中以具体类型MyHelper注册IMyHelper服务。

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<IMyHelper, MyHelper>();
    ...
}

第四,创建一个私有变量以引用服务实例。将服务作为构造函数中的参数传递(通过构造函数注入),然后使用服务实例初始化变量。通过私有变量引用此自定义类实例的任何属性或调用方法。

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;
    private readonly IMyHelper _myHelper;

    public MessageCenterController(
        IOptions<MyOptions> options,
        IMyHelper myHelper
    )
    {
        _options = options.value;
        _myHelper = myHelper;
    }

    public void DoSomething()
    {
        if (_myHelper.CheckIt())
        {
            // Do Something
        }
    }
}

答案 4 :(得分:0)

您可以使用Activator.CreateInstance()。这是一个包装函数。使用方法如下。

var definedProgrammatically =“ My.NameSpace.DemoClass1”; //实现IDemo接口 var obj = CreateInstance (以编程方式确定,“这进入构造函数的参数。”,“如果您的类位于当前程序集中,则忽略此参数”);

现在,您有一个obj实例,该实例从以编程方式确定的类型实例化。可以将该obj注入非控制器类中。

public TInterface CreateInstance<TInterface, TParameter>(string typeName, TParameter constructorParam, string dllName = null)
{
    var type = dllName == null ? System.Type.GetType(typeName) :
            System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName.StartsWith(dllName, System.StringComparison.OrdinalIgnoreCase)).GetType(typeName);
    return (TInterface)System.Activator.CreateInstance(type, constructorParam);

}

PS:您可以遍历System.AppDomain.CurrentDomain.GetAssemblies()以确定容纳您的类的程序集的名称。该名称在包装函数的第3个参数中使用。