【问题标题】:DI parameters to class library without controllerDI参数到没有控制器的类库
【发布时间】:2019-06-11 04:56:48
【问题描述】:

所以我不确定我是否只是遗漏了一些东西,但基本上我在 asp.net core 中看到的每个 DI 示例都显示了从 appSettings.json 文件通过控制器中的构造函数传递参数,然后传递给任何东西否则。

我可以绕过控制器直接注入一个类库吗?

举一个我想要做的例子,假设我有 appSettings.json

"EmailSettings":{"smtpServer":"mail.example.com", "port":123, "sendErrorsTo":"errors@example.com"}

然后是 EmailServices 的类库

EmailSettings.cs

public class EmailSettings{
   public string smtpServer {get;set;}
   public int port {get;set;}
   public string sendErrorsTo {get;set;}
}

IEmailService.cs

public interface IEmailService
{
   void SendErrorEmail(string method, Exception ex);
}

和 EmailService.cs

public class EmailService :IEmailService
{
   private readonly EmailSettings _emailSettings;
   public EmailService(EmailSettings emailSettings)
   {
      _emailSettings = emailSettings;
   }
   public void SendErrorEmail(string method, Exception ex)
   {
      ....
   }
}

主 asp.net 核心应用程序中的 Startup.cs

public void ConfigureServices(IServiceCollection services)
{
   ...
   services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
   services.AddScoped<IEmailService, EmailService>(p => {
      return new EmailService(p.GetService<EmailSettings>());
   });
   ...
}

无需通过控制器加载 EmailServices 或 appsetting.json 参数,然后加载到 BusinessLayer 类库中,我希望能够从 BusinessLayer(或任何其他地方)调用 SendErrorEmail。

DoWork.cs

public MakeItWork()
{
   try
   {...}
   catch (exception ex)
   {
      IEmailService.SendErrorEmail("BAL - MakeItWork",ex)
   }
}

但它只是失败了一个空异常。启动中的 DI 并没有创建 EmailService 来代替 IEmailService,我猜参数也不在那里。

感谢您提供的任何帮助。

----编辑----

我最终只是切换到使用 AutoFac 进行 DI。它能够完成我一直在寻找的东西。接受下面的答案,为 Phantom 提供尝试提供帮助的分数。

【问题讨论】:

    标签: dependency-injection asp.net-core-2.1 class-library


    【解决方案1】:

    有几点:

    • 在您的MakeItWork() 方法中,您有使用接口名称“调用”方法的代码——甚至不确定如何编译。您需要使用实现该接口的类的对象在运行时实际进行方法调用。例如,在您的 DoWork 类中,您可以有一个构造函数来请求实现 IEmailService 接口的类的实例并将其存储以供将来在其他方法中使用。

    • 其次,在 Services 集合中,您将添加一个“作用域”依赖项(在 ConfigureServices 方法中)。 “作用域”依赖项仅在 (http)Request 上创建,通常通过调用控制器来创建。从您的代码和解释来看,您似乎想要为您的IEmailService 接口添加一个单例对象。因此,不要添加 Scoped 依赖项,而是使用 AddSingleton - 正如您所做的那样,您还可以在对 AddSingleton 的调用中创建特定对象 - 这意味着每次请求时都会提供该对象(通过类构造函数,例如)。如果您将它用作单例,您还应该确保它是线程安全的。或者,您也可以使用 AddTransient 添加依赖项 - 如果您使用它,则每次请求时都会创建一个 new 对象。

    更新: 示例代码

    修改您的 ConfigureServices 以使 EmailService 为 Transient(这意味着每次请求此服务时都会创建一个新对象):

    public void ConfigureServices(IServiceCollection services)
    {
       ...
       services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
       services.AddTransient<IEmailService, EmailService>();
       ...
    }
    

    您的“DoWork”类应该在构造函数中请求电子邮件服务:

    public class DoWork()
    {
    
    private IEmailService _emailService;
    
    //Dependency should be injected here
    public DoWork(IEmailService emailService)
    {
        _emailService = emailService;
    }
    
    
    public MakeItWork()
    {
       try
       {...}
       catch (exception ex)
       {
          //Use the saved email service object to do your work
          _emailService.SendErrorEmail("BAL - MakeItWork", ex)
       }
    }
    
    }
    

    它并没有在这里结束。关于如何创建 DoWork 类的 Object 的问题仍然存在。为此,一个想法是为 DoWork 类本身创建一个接口,然后也为该接口设置容器。然后,无论您想在哪里使用 DoWork 实现,您都可以“请求”DoWork 的接口。或者直接使用容器创建实例。

    【讨论】:

    • 也许我完全误解了,但我虽然从 Startup.cs 那里的 DI 意味着任何时候调用 IEmailService,然后使用 EmailService,并且这一行“return new EmailService(p.GetService ())" 应该意味着,将这些参数传递给 EmailService 构造函数。我是否完全误解了它应该做什么?
    • 戴夫,我认为在尝试让您的代码工作(甚至了解您想要什么)之前了解基础知识可能是个好主意。 A good place to start is here.
    • 去过那里多次,几个 SO 页面,来自其他开发人员和 PluralSight 的几个博客。发布问题以查看这是否可能基本上是最后的手段。
    • 为您添加了更多注释(带有示例代码 - 更新了原始答案)。
    • 感谢您的更新。似乎无论哪种方式,我都必须在控制器上注入一些东西才能将其传递给 BAL。我似乎无法做任何事情,比如调用 EmailService 的静态方法。我之前尝试为 DoWork 创建一个接口并将 EmailService 直接注入其中,但仍然必须将 IDoWork 放入控制器的构造函数中才能使用它。 - 再次,除非我错过了什么。我正处于可以正常工作的地步,只是不是我想象的那样。
    猜你喜欢
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多