题
我这里有一个很常见的情况。多年来,我一直没有发现我所做的事情是否符合行业标准。考虑一个连接到数据库的应用程序,但连接字符串不是存储在某些文件/设置中,而是作为命令行参数传递在启动时或在应用程序启动时浏览数据库。
那么有必要将该连接字符串保存在应用程序范围内的某个位置。我见过的最常见的方法是使用模块或全局类,使用 get/set 方法来保存连接字符串。我会做的另一种方法是使用 Singleton。无论哪种选择,我的 DAL 都可以在需要时通过 GetConnectionString 方法访问连接字符串。
有更好的方法吗?
更新: 我没有配置文件,即使有,我也需要在应用程序实例的生命周期内读取一次连接字符串。你能详细说明一下“将它注入到任何类中”部分吗
解决方案
在一般的全局状态下,无论是全局类还是单例,都应该尽可能避免。
理想的解决方案是让您的应用程序从配置中加载连接字符串并 注入 它进入任何需要它的类。根据您的应用程序的大小, 国际奥委会 类似容器 统一 或者 温莎城堡 可以提供帮助,但肯定不是解决方案的必需部分。
如果这不是一个选择,并且您被迫维护全局状态(由于现有的代码库或其他原因),我不知道您建议的两种方法之间存在巨大差异。
更新: 只是为了澄清一下,暂时忘记有关 IoC 容器的所有内容,“注入”只是“作为参数传递”的一种奇特方式(或者传递给类的构造函数,或者通过属性,或者其他方式)。
数据访问类不必要求连接字符串(来自某种全局或单例),而是通过构造函数或属性传入。
更新#2: 我认为对于这种方法的含义仍然存在一些误解。
它基本上取决于您的数据访问类是否如下所示:
public class DataAccessClass
{
public DataAccessClass()
{
_connString = SomeStaticThing.GetConnectionString();
}
}
或者
public class DataAccessClass
{
public DataAccessClass(string connString)
{
_connString = connString;
}
}
这些 文章 (事实上,该博客中的许多文章)详细说明了后者优于前者的许多原因(尤其是因为前者几乎不可能进行单元测试)。
是的,在 某个地方 首先必须有一些静态人员负责获取连接字符串,但关键是您对静态方法的依赖仅限于该一个点(这可能是该过程中的 Main 方法)引导您的应用程序),而不是散布在整个代码库中。
其他提示
很高兴看到如此简单的问题有这么多创造性的解决方案;-)
来自OP问题的显着事实:
- 连接字符串在命令行上传递
- 许多其他组件可能需要使用连接字符串
所以, 没有办法绕过静态元素的使用;无论它是全局变量(在 .NET 中确实很难做到,没有封闭类)、静态类还是单例,这并不重要。最短路径解决方案是由处理命令行的 Program 类初始化的静态类。
所有其他解决方案仍然需要静态访问传入的连接字符串, ,尽管它们可能会将其隐藏在一层或多层间接层后面。
我并不是说您不想用更奇特的东西来装饰基本解决方案,但这是没有必要的,并且它不会消除连接字符串的基本静态/全局性质 如上所述.
如果你要存储是一个字符串(可能连同一堆其他的全局应用程序设置),我只想用一个静态类与一群属性来容纳所有。单身是更多的代码和维护工作,但它是在这种情况下不必要的工作,因为你的属性的类是不会做任何事情;刚装东西。
当然,有一种方法。首先,加快速度上依赖注入和类似的技术,并阅读有关的服务层因为那是什么您这里需要。
至于服务层而言,你需要某种配置服务,这将是您的应用程序的部分将用于配置信息查询模块 - 连接字符串的URI等:
interface IConfigurationService
string ConnectionString
bool IntegratedSecurity
bool EncryptProfiles
这应该是非常简单的,有一个只有在你的系统IConfigurationService
的实例,但是这与选择的DI容器来实现,而不是与Singleton模式。
接下来,所有的DAL服务将获取到IConfigurationService
参考:
class FooDal : IFooDal
IConfigurationService ConfigurationService
,以便他们现在可以使用IConfigurationService.ConnectionString
来获取连接字符串的保持。
这取决于。
请记住,单例:
- 强制该类只存在一个实例,并且
- 提供全球访问
全局仅提供全局访问,但不保证实例化的数量。
当然,最后的选择是使用局部变量。也就是说,将配置信息作为参数传递给构造函数或任何需要的地方。
在前两者之间进行选择应该比较容易。如果该类存在两个实例,这会是一场灾难吗?我想说可能不会。我可能想实例化我自己的配置类,以便为应用程序的一个特定组件提供单独的选项,或者初始化我的单元测试。
极少数情况下您 需要 以保证只有一个实例存在。因此,全局可能是比单例更好的选择。
本地和全局之间的选择更加棘手。通常最好避免全局可变状态。 不可变的 状态不是一个问题,并且不会导致与全局可变状态相同的同步/并发/可伸缩性问题。
但通常情况下,最好将其作为局部变量传递给需要它的组件。这可以手动完成,只需将其传递给需要数据库访问的对象的构造函数,或者在某种程度上可以使用 IoC 容器自动完成。
但无论如何,如果它不是全局的,那么您的代码就会变得更加通用,并且更加可移植。如果它隐藏了对类和全局数据的依赖关系 必须 存在才能使代码正常工作,那么它就不能轻易地在其他项目中使用,或者如果您对整个代码库进行了过多的重构。
单元测试也变得更加困难,因为除非存在一些我们希望在测试中排除的外部数据,否则代码甚至无法编译。
作为@Anton说,你需要有一个公开类似
的接口interface IConfigurationService
string ConnectionString
然后,每当你的类的人需要你的连接字符串,你为它提供在包含有效的字符串建设IConfigurationService的实现。你需要找到一个有效的地方创建您的实现,当你的应用程序启动(可能是时间,你会得到你的数据库ADRESS),并把它转嫁给类需要它。
即使它可能看起来很多工作相比,单身或全球,这将降低耦合在你的代码,因此提高了可重用性,使单元测试更加简单,并且是非常简单的,一旦你说服自己,全局的(大部分)邪: )
由于之前mentionned,有IoC容器,将提供一个框架,要做到这一点,但它可能在你的情况是矫枉过正,加上其不错的让工作提高到一个“魔盒”前使用该模式为自己