5

我这里有一个很常见的情况。多年来我一直没有发现我正在做的事情是否符合行业标准。考虑一个连接到数据库的应用程序,但是连接字符串而不是存储在某些文件/设置中作为命令行参数传递在启动时或在应用程序启动时浏览数据库。

那么有必要将该连接字符串保存在应用程序范围内的某个位置。我见过的最常见的方法是使用 get/set 方法保存连接字符串的模块或全局类。我会这样做的另一种方法是使用单例。我的 DAL 可以在需要时通过 GetConnectionString 方法访问连接字符串。

有更好的方法吗?

更新:我没有配置文件,即使我有,我也需要在应用程序实例的生命周期内读取一次连接字符串。您能否详细说明“将其注入任何类”部分

4

6 回答 6

8

一般来说,全局状态,无论是全局类还是单例,都应该尽可能避免。

理想的解决方案是让您的应用程序从配置中加载连接字符串并将其注入任何需要它的类中。根据应用程序的大小,像UnityCastle Windsor这样的IoC容器可以提供帮助,但肯定不是解决方案的必需部分。

如果这不是一个选项,并且您坚持维护全局状态(由于现有的代码库或其他原因),我不知道您建议的两种方法之间存在巨大差异。

更新:澄清一下,暂时忘记有关 IoC 容器的所有内容,“注入”只是“作为参数传入”的一种奇特方式(传递给类的构造函数,或通过属性,或其他方式)。

与其让数据访问类必须请求连接字符串(来自某种全局或单例),不如让它通过构造函数或属性传入。

更新#2:我认为对于这种方法的含义仍然存在一些误解。

它基本上归结为您的数据访问类是否如下所示:

public class DataAccessClass
{
    public DataAccessClass()
    {
        _connString = SomeStaticThing.GetConnectionString();
    }
}

或者

public class DataAccessClass
{
    public DataAccessClass(string connString)
    {
        _connString = connString;
    }
}

这些 文章(事实上,该博客中的许多文章)详细说明了后者优于前者的一些原因(尤其是因为前者几乎不可能进行单元测试)。

是的,在某些地方,首先必须有一些静态人员负责获取连接字符串,但关键是您对静态方法的依赖仅限于那个位置(这很可能是您的引导应用程序过程中的主要方法),而不是散布在整个代码库中。

于 2009-02-02T17:31:09.723 回答
5

很高兴看到这么简单的问题有这么多创造性的解决方案;-)

来自OP问题的显着事实:

  1. 连接字符串在命令行上传递
  2. 许多其他组件可能需要使用连接字符串

所以,没有办法绕过使用静态元素;无论是全局变量(在 .NET 中很难做到,真的,没有封闭类)、静态类还是单例都无关紧要。最短路径解决方案是由处理命令行的 Program 类初始化的静态类。

每个其他解决方案仍然需要对传入的 connect-string 进行静态访问,尽管它们可能会将其隐藏在一个或多个间接层之后。

我并不是说您不想用更花哨的东西来修饰基本解决方案,但这不是必需的,它并没有消除所描述的连接字符串的基本静态/全局性质。

于 2009-02-02T18:38:31.680 回答
4

如果您要存储的只是一个字符串(可能还有一堆其他全局应用程序设置),我只会使用一个带有一堆属性的静态类来保存所有这些。单例需要更多的代码和维护工作,但在这种情况下这是不必要的工作,因为您的属性类不会做任何事情;只是拿着东西。

于 2009-02-02T17:31:42.517 回答
2

当然有办法。首先,了解依赖注入和类似技术的速度,并阅读有关服务层的信息,因为这就是您在这里所需要的。

就服务层而言,您需要某种配置服务,它将是您的应用程序的一部分将查询配置信息的模块 - 连接字符串、URI 等:

interface IConfigurationService
    string ConnectionString
    bool IntegratedSecurity
    bool EncryptProfiles

您的系统中只有一个实例应该非常简单IConfigurationService,但这是通过选择的 DI 容器实现的,而不是使用单例模式。

接下来,您的所有 DAL 服务都将获得对以下内容的引用IConfigurationService

class FooDal : IFooDal
    IConfigurationService ConfigurationService

以便他们现在可以使用IConfigurationService.ConnectionString来获取连接字符串。

于 2009-02-02T17:33:52.807 回答
1

这取决于。

请记住,单例:

  • 强制该类的一个实例将存在,并且
  • 提供全球访问

全局只提供全局访问,但不保证实例化的数量。

最后的选择当然是使用局部变量。也就是说,将配置信息作为参数传递给构造函数或任何需要的地方。

在前两者之间进行选择应该有点容易。如果该类的两个实例存在,那会是一场灾难吗?我会说可能不会。我可能想实例化我自己的配置类来为应用程序的一个特定组件提供单独的选项,或者初始化我的单元测试。

在极少数情况下,您需要保证只存在一个实例。出于这个原因,全局可能是比单例更好的选择。

本地和全球之间的选择比较棘手。全局可变状态通常最好避免。不可变状态的问题较小,并且不会导致与全局可变状态相同的同步/并发/可伸缩性问题。

但通常,最好将其作为局部变量传递给需要它的组件。这可以手动完成,只需将其传递给需要 DB 访问的对象的构造函数,或者在某种程度上可以使用 IoC 容器自动化。

但无论如何,如果它不是全局的,那么你的代码就会变得更通用,更便携。如果它对代码工作必须存在的类和全局数据具有隐藏的依赖关系,那么它就不能轻易地在其他项目中使用,或者如果你过多地重构整个代码库。

单元测试也变得更加困难,因为代码甚至无法编译,除非存在一些我们理想情况下希望从测试中排除的外部数据。

于 2009-02-02T17:35:11.480 回答
0

正如@Anton 所说,你需要有一个接口来暴露类似的东西

interface IConfigurationService
    string ConnectionString

然后,每当您的类中的一个需要您的连接字符串时,您就可以在包含有效字符串的构造时为其提供 IConfigurationService 的实现。您需要在应用程序启动时(可能是您获得数据库地址的时间)找到一个有效的位置来创建您的实现,并将其传递给需要它的类。

尽管与单例或全局相比,这似乎需要做很多工作,但这会降低代码中的耦合度,从而提高可重用性并使单元测试更容易,并且一旦你说服自己全局(大部分)是邪恶的,那就非常简单了 :)

如前所述,有 IoC 容器将提供框架来执行此操作,但在您的情况下它可能有点矫枉过正,而且在将工作交给“魔术盒”之前为自己使用该模式很不错

于 2009-02-02T18:11:39.050 回答