如何将此静态类方法转换为依赖注入? (包含特定代码)

时间:2016-08-09 14:39:52

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

(我确信我严重格式化了这个问题,我很乐意根据评论进行修改和修改)

我有一个静态类,我正在尝试使用依赖注入来改进设计。我不一定希望这个类是静态的,因为我将使用.NET Core,它可以在静态类情况下提升依赖注入。

.NET(非核心)中的简化代码:

public static class Utils
    {
    public static readonly string tokenUrl = ConfigurationManager.AppSettings["tokenUrl"];
    public static readonly string tokenKey = ConfigurationManager.AppSettings["tokenKey"];

    public async static Task<bool> SendEmail(Email email)
        {
            var http = new HttpClient();
            http.DefaultRequestHeaders.Add("subscription-key", tokenKey);

            try
            {
                await http.PostAsync(tokenUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
            }
            catch (Exception e)
            {
                return false;
            }

            return true;
        }
    }

对于ConfigurationManager.AppSettings(它在.NET Core中不存在),我打算在此链接中使用该方法:http://www.danylkoweb.com/Blog/no-configurationmanager-in-aspnet-core-GC

但是,为了将此(SendMail)方法转换为依赖注入,我很遗憾。我已经阅读了很多示例和文章,我理解了依赖注入的逻辑,但我不知道如何将这个静态类转换为适当的依赖注入。同一个Utils类中还有其他方法,但这是最简单的方法,我希望能够找出使用这个方法的其他方法。

我想到的一种方法是:

public interface ISendMail
{
    FormSettings ConfigSettings { get; set; }

    Task<bool> SendEmail(IOptions<FormSettings> settings, Email email);

}

public class SendEmail : ISendMail
{
    public async static Task<bool> SendEmail(IOptions<FormSettings> settings, Email email)
    {
        //do same things
    }
}

但我很清楚,因为它甚至没有意义。我想到的另一种方法是:

public class SendEmail
{
    FormSettings ConfigSettings { get; set; }
    protected Email email = null;

    public SendEmail(IOptions<FormSettings> settings, Email email)
    {
        ConfigSettings = settings.Value;
        this.email = email;
    }

    public async static Task<bool> SendEmailAction()
    {
        //do same things with "email" and "ConfigSettings"
    }
}

我知道我在这里提供了很多代码,我不确定是否应该在“代码审查”中提出这个问题。我最关心的是 FormSettings 部分,但是以依赖注入格式实现SendEmail的功能。

很快,我怎样才能将这个“SendEmail”类转换为我可以在.NET Core中使用它的格式,而不需要静态类?这个特殊的方法不需要使用.NET Core进行更改,但我的其他方法也是如此,这就是我试图摆脱静态类方法的原因。

我可以排除tokenUrl和tokenKey部分并在需要时简化问题,我对如何处理这种情况感到很遗憾。

2 个答案:

答案 0 :(得分:2)

这堂课该怎么办?发送电子邮件,对吧?所以界面:

public interface IEmailSender
{
    Task<bool> Send(Email email);
}

我们如何实施它?像这样:

public class MyEmailSenderOne : IEmailSender
{
    public static readonly string tokenUrl = ConfigurationManager.AppSettings["tokenUrl"];
    public static readonly string tokenKey = ConfigurationManager.AppSettings["tokenKey"];

    public async Task<bool> Send(Email email)
    {
        var http = new HttpClient();
        http.DefaultRequestHeaders.Add("subscription-key", tokenKey);

        try
        {
            await http.PostAsync(tokenUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
        }
        catch (Exception e)
        {
            return false;
        }

        return true;
    }
}

public class MyAnotherAwesomeEmailSender : IEmailSender
{
    public async Task<bool> Send(Email email)
    {
        // send with different way
        return true;
    }
}

我们如何注入这个?

public class SomeClass
{
    private IEmailSender _sender;
    public SomeClass(IEmailSender sender)
    {
        _sender = sender;
    }

    public void Foo()
    {
        // do smth useful
        _sender.Send(new Email());
    }
}

UPD。

由于您的电子邮件设置是持久的(在生命周期内不会更改),并且由于此设置仅与您的IEMailSender实施相关,因此您应该在实施中注入它们。试想一下=为什么调用者代码(Controller)应该知道你的实现是如何工作的? 所以

public class MyEmailSenderOne : IEmailSender
{
    private FormSettings _settings;

    public MyEmailSenderOne(IOptions<FormSettings> settings)
    {
        _settings = settings.Value;
    }

    public async Task<bool> Send(Email email)
    {
        var http = new HttpClient();
        http.DefaultRequestHeaders.Add("subscription-key", _settings.tokenApiKey);

        try
        {
            await http.PostAsync(_settings.tokenApiUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
        }
        catch (Exception e)
        {
            return false;
        }

        return true;
    }
}

而且,控制器现在知道你的实现的任何设置,它看起来像

public class CommunicationsController : Controller
{
    private IEmailSender _sender;

    public CommunicationsController(IEmailSender sender)
    {
        _sender = sender;
    }

    public async Task<ActionResult> ContactUsFormSubmit(ContactUs request)
    {
            ...
                    request.EmailSent = await _sender.SendEmail(new Email() { TemplateId = 3, Body = JsonConvert.SerializeObject(request) });
            ...
    }
}

正如您所看到的,控制器现在非常干净,您可以轻松地将IEmailSender的实现更改为任何其他控制器代码。这是使用DI的优势之一。

答案 1 :(得分:0)

根据tym32167的回答,我能够实现IEmailSender功能(最后)。我仍然会选择他的答案作为正确的答案,但这就是我实现依赖注入的方式。

如果您想了解我正在使用的IOptions和FormSettings类的更多信息,请阅读我在问题中提供的链接。

这是界面和类:

public interface IEmailSender
    {

        Task<bool> SendEmail(Email email, FormSettings settings);

    }

    public class EmailSender : IEmailSender
    {

        FormSettings ConfigSettings { get; set; }

        public async Task<bool> SendEmail(Email email, FormSettings settings)
        {

            var http = new HttpClient();
            http.DefaultRequestHeaders.Add("subscription-key", settings.tokenApiKey);

            try
            {
                await http.PostAsync(settings.tokenApiUrl + "email", new StringContent(JsonConvert.SerializeObject(email), Encoding.UTF8, "application/json"));
            }
            catch (Exception e)
            {
                return false;
            }

            return true;
        }
    }

在控制器注入中:

public class CommunicationsController : Controller
    {

        private IEmailSender _sender;
        private FormSettings ConfigSettings { get; set; }

        public CommunicationsController(IEmailSender sender, IOptions<FormSettings> settings)
        {
            _sender = sender;
            ConfigSettings = settings.Value;
        }

public async Task<ActionResult> ContactUsFormSubmit(ContactUs request)
        {
            ...
                    request.EmailSent = await _sender.SendEmail(new Email() { TemplateId = 3, Body = JsonConvert.SerializeObject(request) }, ConfigSettings);
            ...
        }

以下是FormSettings,以便在链接死亡时轻松参考:

public class FormSettings
{
    public string tokenApiUrl { get; set;  }
    public string tokenApiKey { get; set;  }
}

我希望我没有错过任何细节,到目前为止它没有给我任何错误,但由于我们没有在项目中进行单元测试,我将无法立即测试。如果答案遗漏了,请告诉我。