我的应用程序的app.config文件中有一些键可以容纳电子邮件设置.我目前正在开发的应用程序有几个地方我需要发送一封电子邮件(主要是在错误检查中让我们知道发生了一些不好的事情).为了更容易获取我需要它的设置(而不是回到app.config查找要使用的文本字符串,ConfigurationManager.AppSettings["SomeKey"].ToString()我创建了一个简单的静态类来容纳一些readonly属性,返回我需要的值.这是基本结构
internal static Class myClass
{
internal static string setting1
{
get { return ConfigurationManager.AppSettings["SomeKey"].ToString(); }
}
internal static string setting2
{
get { return ConfigurationManager.AppSettings["SomeOtherKey"].ToString(); }
}
}
Run Code Online (Sandbox Code Playgroud)
我这样做是否可以接受?我找不到这个问题,但不禁想到我错过了什么.
附加信息:
我知道简单地使用Settings.Settings文件的方法.由于我工作的代码策略,我无法使用Settings.Settings文件,这将使我这样做的方式变得不必要.
编辑:通过"可接受"我的意思是它通常做或被认为是一个好主意?我意识到整个社区都不能推测我工作的可接受性.
Sco*_*nen 13
这不错,但通过小调整可以更好.(好吧,也许不是很小.)
如果你的类依赖于静态类,并且该类依赖于AppSettings那么你的类仍然会被耦合到AppSettings.换句话说,没有其他方法可以让他们获得他们的设置.如果你想对你的班级进行单元测试,那将是一个挑战.这意味着您的单元测试项目必须具有相同的<appSettings>部分.但是,如果您想要两个使用两个不同值进行设置的测试呢?不可能.或者,如果您有一个需要设置的类,并且在几年内您想在ASP.NET Core应用程序中使用它并且没有web.config,该怎么办?然后它根本不起作用.
为了避免这种情况,你可以这样做:
public interface IMySettings
{
string Setting1 {get;}
string Setting2 {get;}
}
public class MyConfigurationSettings : IMySettings
{
public string Setting1
{
get { return ConfigurationManager.AppSettings["SomeKey"].ToString(); }
}
public string Setting2
{
get { return ConfigurationManager.AppSettings["SomeOtherKey"].ToString(); }
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在需要设置的类中:
public class ClassThatNeedsSettings
{
private readonly IMySettings _settings;
public ClassThatNeedsSettings(IMySettings settings)
{
_settings = settings;
}
}
Run Code Online (Sandbox Code Playgroud)
然后,当你创建一个实例时,ClassThatNeedsSettings你传递一个实现的类的实例,IMySettings该类将使用它来检索设置.当您的应用程序运行时,您将传入,MyConfigurationSettings以便您的值来自AppSettings.但ClassThatNeedsSettings从来不知道.它只知道它正在使用的实例IMySettings.
这称为"依赖注入".ClassThatNeedsSettings取决于IMySettings你将它"注入"构造函数.这种方式ClassThatNeedsSettings可以满足它的需要.它不负责创建它.
如果你想进行单元测试,可以"模拟" IMySettings.也就是说,您可以创建实现该接口的其他类,并使用它们传递您要测试的任何值.甚至有像Moq这样的工具可以帮助您创建这些类.
通常,如果使用依赖注入,您还将使用Windsor,Unity,Autofac或其他框架来管理为您创建对象.感觉有点像诱饵和开关在最后介绍它因为它需要更多的学习并且可能改变应用程序的配置方式.但这就是我们使用它的原因,以防止一个类对另一个类具有绝对依赖性,这使得它不那么灵活且难以测试.
以下是解决评论者担忧的可测试代码示例:
interface IEmailSettings {
string Server { get; }
string Sender { get; }
string[] Recipients { get; }
}
Run Code Online (Sandbox Code Playgroud)
如果您将设置存储在 app.config 中,请使用以下命令:
class AppConfigEmailSettings : IEmailSettings {
public AppConfigEmailSettings() {
this.Server = ConfigurationManager.AppSettings["server"];
this.Sender = ConfigurationManager.AppSettings["sender"];
this.Recipients = ConfigurationManager.AppSettings["recipients"].Split(';');
}
public string Server { get; private set; }
public string Sender { get; private set; }
public string[] Recipients { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)
如果您将设置存储在数据库中,请使用以下命令:
class DatabaseEmailSettings : IEmailSettings {
public DatabaseEmailSettings(string connectionString) {
//code to connect to database and retrieve settings
}
//you can use the same fields and properties from AppConfigEmailSettings
}
Run Code Online (Sandbox Code Playgroud)
为了进行测试,您可以使用如下内容:
class MockSettings : IEmailSettings {
public string Server { get { return "localhost"; } }
public string Sender { get { return "sender@example.com" } }
public string[] Recipients { get { return new string[] { "r1@example.com" }; } }
}
Run Code Online (Sandbox Code Playgroud)
你明白了。这段代码比你的更容易测试。此外,如果您注入IEmailSettings发送电子邮件的代码,您可以通过更改整个应用程序中的一行代码来轻松更改存储电子邮件设置的方式。IEmailSettings这是将对象实例化为AppConfigEmailSettings或DatabaseEmailSettings或其他东西的行。