.NET / LINQ-SQL / ASP.NET 中的连接字符串地狱

Posted

技术标签:

【中文标题】.NET / LINQ-SQL / ASP.NET 中的连接字符串地狱【英文标题】:Connection string hell in .NET / LINQ-SQL / ASP.NET 【发布时间】:2010-09-19 04:21:49 【问题描述】:

我有一个包含以下内容的网络应用程序:

一个 Web 项目(带有一个包含连接字符串的 web.config 文件 - 但该 Web 项目中没有数据访问代码) 使用 LINQ-SQL 类向 Web 项目 UI 提供实体的数据访问项目(该项目有一个设置文件和一个 app.config - 两者都有连接字符串)

当我构建和部署时,Bin 目录中没有设置文件或 app.config 与数据访问 .dll,但更改 web.config 文件中的连接字符串不会相应地更改数据库 - 所以连接字符串必须编译到数据访问 dll 中。

我需要一个用于我的整个部署的配置文件——网站、数据访问 dll、一切——它有一个可以使用的连接字符串。目前,似乎有多个连接字符串在各处被使用或硬编码。

我怎样才能最好地解决这个烂摊子?

感谢您的帮助。

【问题讨论】:

这似乎是 linq to sql 中的一个错误,我在 linq to entity 上做了很多工作,这是我第一次使用 linq to sql 并第一次遇到这个问题。 【参考方案1】:

如何定义一个 ConnectionFactory 对象,它将一个枚举作为参数并返回一个完整的连接对象?

【讨论】:

【参考方案2】:

启动项目的配置文件将定义所有包含项目的配置设置。例如,如果您的 Web 项目是启动项目,则对“appSettings”的任何引用都会从 web.config 中查找设置,这包括从您的数据访问项目中对“appSettings”的任何引用。因此,将任何配置设置从数据访问项目的 app.config 复制到 Web 项目的 web.config。

【讨论】:

【参考方案3】:

这是看待它的一种方式。哪个组件应该决定使用哪个数据库?数据库(或至少连接字符串)将来可能会更改。网站是否决定使用哪个数据库?或者,由 DAL 决定?

如果您有 dev、QA、UAT 和 prod 数据库,管理这些连接字符串至关重要。

如果网站决定,它应该将连接字符串从其 web.config 传递到 DAL。 如果网站不应该知道或关心数据的来源,则连接字符串属于 DAL。

【讨论】:

【参考方案4】:

您还可以让 Web 应用程序在需要使用数据访问项目时提供连接字符串。你可以让它成为构造函数的一部分。

此外,您可以编写自己的逻辑,在数据访问项目进行调用时从外部文件加载连接字符串。

【讨论】:

【参考方案5】:

您的应用程序将仅使用 web.config 文件中的配置条目。 只要结构正确,您就可以将 dll 配置设置放在 web.config 文件中。我的示例是特定于 VB 的,使用 My Namespace,但它为您提供了总体思路。

在配置文件的 configSections 部分中,您需要一个条目:

<configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
        <section name="YourAssembly.My.MySettings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup></configSections>

然后在配置文件的 applicationSettings 部分中放置每个 dll 的条目:

    <applicationSettings>
      <YourAssembly.My.MySettings>
        <setting name="DebugMode" serializeAs="String">
            <value>False</value>
        </setting>
      </YourAssembly.My.MySettings>
    </applicationSettings>  

【讨论】:

【参考方案6】:

在一个完美的世界里,我认为你会重构你的数据层以通过 System.Configuration 或相关的构造函数/工厂来获取配置设置。这意味着,您要么需要重新连接其隐式配置源,要么显式设置来自其主机/消费者的连接。集中这些类型常量的另一个相关模式是将只读属性放入静态帮助器类,并让该类管理来自配置等的实际解析。

NHibernate 及其配置/映射管理是我认为可以很好地展示如何优雅地做到这一点的一个地方。诚然,这有点像 xml 地狱,而且 Fluent NHib 更甜美,但大多数现实世界的示例将向您展示如何协调支持程序集与执行程序集的配置。

【讨论】:

【参考方案7】:

我从来没有遇到过 数据访问层 (DAL) 能够使用我的web.config 文件中的连接字符串的问题。通常我只是从 DAL 中复制连接字符串部分并将其粘贴到 web.config 中。我正在使用 DBML 设计器来创建数据上下文。

如果这对您不起作用,您可以在数据上下文构造函数中指定连接字符串。在您的 Web 项目中有一个静态类来加载您的设置,包括您的连接字符串,当您创建 DAL 对象(或数据上下文,如果直接创建它)时,只需将其传递给构造函数。

public static class GlobalSettings

    private static string dalConnectionString;
    public static string DALConnectionString
    
       get
       
           if (dalConnectionString == null)
           
              dalConnectionString = WebConfigurationManager
                                      .ConnectionStrings["DALConnectionString"]
                                        .ConnectionString;
           
           return dalConnectionString;
       
    

...

using (var context = new DALDataContext(GlobalSettings.DALConnectionString))

   ...

【讨论】:

啊,我刚刚意识到 DAL = 数据访问层。我们新手类型的语法有点慢。 对不起,我很愚蠢,但是我应该把这个类放在 web 应用程序项目或数据访问项目中的什么地方。您对如何覆盖 DALDataContext() 默认构造函数以默认为 web.config 中的字符串有任何想法吗?谢谢 @Kieran -- 因为它只关心网络设置,所以我会把它放在网络项目中。 (@tvanfosson:这是题外话,但我想我还是要提一下:如果您的 GlobalSettings 代码应该实现单例模式,那么它就不是线程安全的。Jon Skeet 写了一个article on how to implement thread-safe singletons;使用Lazy&lt;T&gt; 类型也可以很好地工作。) @Stakx - 这是一个只读属性。即使另一个线程替换了该值,它也会用相同的东西替换它。您可以将其包装在 lock 语句中,但我认为这不是问题。【参考方案8】:

基于 .config 文件滚动您自己的 ConnectionFactory:

定义自定义配置部分以映射键/连接字符串对 教您的 ConnectionFactory 根据需要使用主机名或机器名来嗅探该配置部分 为您的各种 dev/qa/prod 服务器填充键/连接字符串值,并将它们放入您的各种 app.config、web.config 等文件中。

专业版:

所有人都生活在项目中,所以没有惊喜 添加其他部署目标是在 .config 文件中的复制/粘贴操作

缺点:

造成大而丑陋的 XML 部分,尤其是在您有十几个生产服务器的情况下 需要在项目之间复制 需要更改代码并重新部署以添加新目标 代码需要了解它的生存环境

【讨论】:

【参考方案9】:

根据注册表滚动你自己的 ConnectionFactory:

在 SOFTWARE/[YOUR_COMPANY]/[YOUR_APP] 下为您的应用程序添加注册表项 为 ConnectionString 添加一个字符串值 教您的 ConnectionFactory 破解打开适当的注册表项(在静态构造函数中,而不是每次加载页面!)。 将注册表信息导出为 .reg 文件,将其添加到源代码管理中,根据需要对其进行修改和应用以设置其他计算机。

专业版:

设置简单 连接字符串位于一个地方 不在 web/app.config 中,因此无需对特定于环境的设置进行硬编码。 不在 web/app.config 中,因此 Junior Dev Jimmy 不会意外告诉您的生产服务器查看 DEV 数据库

缺点:

注册表中存在重要的东西并不是很明显,因此新开发者需要指导。 配置新部署计算机时的额外步骤 注册表是 oldskool。初级开发者会嘲笑你。

【讨论】:

@Jason:感谢提交 2 个答案。深思熟虑的利弊。一个不幸的骗局将是“非网络托管”友好的。【参考方案10】:

感谢您的回复。

那些说应用程序将使用 web.config 中的设置的人对于我在自己的代码中引用它的情况是正确的:

_connectionString = ConfigurationManager.AppSettings["ConnectionString"];

..但是 LINQ-SQL 数据上下文存在不同的问题 - 我认为它们在已编译的 dll 中包含连接字符串,以便在无参数构造函数中使用。正如 tvanofosson 所说,我需要通过传入对 web.config 中连接字符串的引用来创建数据上下文。这就是我陷入困境的地方:)

【讨论】:

是的,如果使用正确名称查找 ConnectionString 的所有其他方法都失败,则 datacontext 将包含 ConnectionString 作为后备 - 您始终可以使用将连接字符串作为参数。【参考方案11】:

我在这个问题上也遇到了一些困难。我通过使用 c# 部分类定义并扩展 dbml 设计器创建的数据上下文找到了解决方案。该解决方案与 tvanfosson 的答案非常相似。您需要做的是创建部分数据上下文类,使用默认构造函数从设置中获取 ConnectionString,并在 dbml 设计器 DC 属性中将连接设置为无。这样连接字符串就不会被编译成dll。 Datacontext 将自动从 web.config 连接字符串设置中获取连接字符串。我还没有测试这是否也适用于 app.config,但我认为它应该可以正常工作。

这里是部分 DC 类的示例:

namespace MyApplication 
    /// <summary>
    /// Summary description for MyDataContext
    /// </summary>
    /// 
    public partial class MyDataContext
    
        public MyDataContext() :
            base(global::System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString, mappingSource)
        
            OnCreated();
        
    

【讨论】:

添加该构造函数不会导致编译问题,因为设计器生成的部分类已经具有相同的构造函数? 没错,根据您选择的设计器属性(连接部分),生成的代码可能会自己创建一个无参数构造函数。 这是我的首选解决方案。如Hihu所述,在设计器中将连接设置为“无”将防止自动生成无参数构造函数,因此不存在编译问题。 here 描述了一个类似的解决方案,具有更详细的细节。【参考方案12】:

为了保证它不受自动生成代码中任何内容的影响,请覆盖数据上下文的 OnCreated() 方法中的连接信息:

using System.Configuration;
namespace MyApplication 

    partial void OnCreated()
    
        // attempt to use named connection string from the calling config file
        var conn = ConfigurationManager.ConnectionStrings["MyConnectionString"];
        if (conn != null) Connection.ConnectionString = conn.ConnectionString;
    

通过这种方式,dbml 设计器可以按照自己的方式进行连接(这在 Web 项目之外并不好),但您可以在应用程序运行时获得连接的最终控制权。

【讨论】:

【参考方案13】:

我知道这很旧,但我就是这样做的(我非常喜欢@Seba 的方式,但我没有尝试过)

这假设您的 DBML 文件位于其自己的类库中,我发现在跨多个网站和其他类库共享实体和数据访问时最方便。它还假设您在每个项目中都将连接字符串命名为相同。当我部署到不同的环境时,我使用 NAnt 来设置它。

我基于上面来自@tvanfosson 的最佳答案 - 向那个人致敬。

    创建您自己的基类,它派生自 LinqDataContext

这是 VB 代码:

    Imports System.Configuration

Public Class CustomDataContextBase
    Inherits System.Data.Linq.DataContext
    Implements IDisposable

    Private Shared overrideConnectionString As String

    Public Shared ReadOnly Property CustomConnectionString As String
        Get
            If String.IsNullOrEmpty(overrideConnectionString) Then
                overrideConnectionString = ConfigurationManager.ConnectionStrings("MyAppConnectionString").ConnectionString
            End If

            Return overrideConnectionString
        End Get
    End Property

    Public Sub New()
        MyBase.New(CustomConnectionString)
    End Sub

    Public Sub New(ByVal connectionString As String)
        MyBase.New(CustomConnectionString)
    End Sub

    Public Sub New(ByVal connectionString As String, ByVal mappingSource As System.Data.Linq.Mapping.MappingSource)
        MyBase.New(CustomConnectionString, mappingSource)
    End Sub

    Public Sub New(ByVal connection As IDbConnection, ByVal mappingSource As System.Data.Linq.Mapping.MappingSource)
        MyBase.New(CustomConnectionString, mappingSource)
    End Sub

End Class
    打开您的 DBML 文件,然后在“属性”中,将上述类名添加到“基类”属性中。

注意,如果您将自定义数据上下文类放在同一个程序集中,只需包含类名,例如自定义数据上下文。

如果它们位于不同的程序集中,请使用完全限定名称,例如MyCo.MyApp.Data.CustomDataContext

    为确保 Designer 的内容正常工作,请将连接字符串复制到类库的 app.config 文件中。这不会在 IDE 之外使用。

就是这样。

您需要将连接字符串命名为相同

您实际上是在强制数据上下文忽略 DBML 文件中设置的连接信息。使用 ConfigurationManager 方法意味着它将从调用程序集中获取连接字符串。

HTH

【讨论】:

以上是关于.NET / LINQ-SQL / ASP.NET 中的连接字符串地狱的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET_基础

ado.net和asp.net区别?

ASP.NET 生命周期 – ASP.NET 应用生命周期

ASP.NET 5 改名 ASP.NET Core 1.0

ASP.NET和ASP的区别是啥?

ASP.NET Core与ASP.NET区别