Ад со строкой подключения в .NET / LINQ-SQL / ASP.NET

У меня есть веб-приложение, которое включает в себя следующее:

  • Веб-проект (с файлом web.config, содержащим строку подключения, но без кода доступа к данным в веб-проекте)
  • Проект доступа к данным, который использует классы LINQ-SQL для предоставления сущностей пользовательскому интерфейсу веб-проекта (в этом проекте есть файл настроек и app.config, оба из которых имеют строки подключения)

Когда я создаю и развертываю, в каталоге Bin с доступом к данным .dll нет файла настроек или app.config, но изменение строки подключения в файле web.config не меняет базу данных соответствующим образом, поэтому строка подключения должна быть скомпилирован в dll доступа к данным.

Мне нужен один файл конфигурации для всего моего развертывания - веб-сайт, библиотеки DLL для доступа к данным, все, что имеет одну строку подключения, которая используется. В настоящий момент, похоже, повсюду используются или жестко запрограммированы несколько строк подключения.

Как мне лучше всего разрешить этот беспорядок?

Спасибо за любую помощь.


person flesh    schedule 25.10.2008    source источник
comment
Кажется, это ошибка в linq to sql, я много работал с linq to entity, и это первый раз, когда я работаю с linq to sql и впервые сталкиваюсь с этой проблемой.   -  person Jitendra Pancholi    schedule 15.12.2014


Ответы (13)


У меня никогда не было проблем с тем, чтобы уровень доступа к данным (DAL) мог использовать строки подключения из моего web.config файла. Обычно я просто копирую раздел строк подключения из DAL и вставляю его в web.config. Я использую конструктор DBML для создания контекста данных.

Если это не сработает, вы можете указать строку подключения в конструкторе контекста данных. В вашем веб-проекте есть статический класс, который загружает ваши настройки, включая строки подключения, и когда вы создаете свой объект 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))
{
   ...
}
person tvanfosson    schedule 25.10.2008
comment
Ах, я только что понял, что DAL = Data Access Layer. Мы, новички, немного медлим с синтаксисом. - person Kyle Ryan; 01.08.2009
comment
Извините за глупость, но где мне разместить этот класс в проекте веб-приложения или в проекте доступа к данным. И есть ли у вас идеи, как переопределить конструктор по умолчанию DALDataContext (), чтобы по умолчанию использовать строку в web.config.? Спасибо - person Kieran; 02.12.2009
comment
@Kieran - Поскольку это касается только веб-настроек, я бы поместил его в веб-проект. - person tvanfosson; 02.12.2009
comment
(@tvanfosson: Это не по теме, но я подумал, что все равно упомяну об этом: если ваш GlobalSettings код должен реализовывать шаблон Singleton, то он не является потокобезопасным. Джон Скит написал статью о том, как реализовать потокобезопасные синглтоны; использование типа Lazy<T> будет тоже работают хорошо и красиво.) - person stakx - no longer contributing; 04.02.2011
comment
@Stakx - это свойство только для чтения. Даже если другой поток заменит значение, он заменит его тем же. Вы можете заключить его в оператор блокировки, но я не думаю, что это будет проблемой. - person tvanfosson; 04.02.2011
comment
(@tvanfossom, я согласен; в данном случае это действительно не проблема. Но если кто-то возьмет приведенный выше код в качестве примера того, как реализовать какой-либо одноэлементный объект, то он может стать одним из них. Вот почему я упомянул об этом и почему мой комментарий тоже не по теме. ;-) - person stakx - no longer contributing; 05.02.2011

Файл конфигурации для запускаемого проекта будет определять параметры конфигурации для всех включенных проектов. Например, если ваш веб-проект является запускаемым, любая ссылка на «appSettings» будет искать настройки из web.config, включая любые ссылки на «appSettings» из вашего проекта доступа к данным. Поэтому скопируйте любые параметры конфигурации из файла app.config проекта доступа к данным в файл web.config веб-проекта.

person Robert Durgin    schedule 25.10.2008

Создайте собственный ConnectionFactory на основе реестра:

  • добавьте раздел реестра для своего приложения в разделе SOFTWARE / [YOUR_COMPANY] / [YOUR_APP]
  • добавить строковое значение для ConnectionString
  • Научите свой ConnectionFactory взламывать соответствующий ключ реестра (в статическом конструкторе, а не при каждой загрузке страницы!).
  • экспортируйте информацию реестра в виде файла .reg, добавьте его в систему управления версиями, измените и примените по мере необходимости для настройки дополнительных компьютеров.

Pro:

  • Просто настроить
  • Connectionstring живет в одном месте
  • Нет в файле web / app.config, поэтому нет необходимости жестко указывать параметры среды.
  • Не в web / app.config, поэтому младший разработчик Джимми не может случайно указать вашему производственному серверу посмотреть базу данных DEV.

Против:

  • Не сразу очевидно, что важные вещи хранятся в реестре, поэтому новым разработчикам потребуются инструкции.
  • Дополнительный шаг при настройке новой машины для развертывания
  • Реестр oldskool. Младшие разработчики будут издеваться над вами.
person Jason Kester    schedule 25.10.2008
comment
@Jason: Престижность за отправку 2 ответов. Хорошо продуманные плюсы и минусы. К несчастью, Con будет "не дружественным к веб-хостингу". - person p.campbell; 30.04.2009

Спасибо за ответы.

Те из вас, кто говорит, что приложение будет использовать настройку в web.config, верны для тех случаев, когда я ссылаюсь на нее в моем собственном коде:

_connectionString = ConfigurationManager.AppSettings["ConnectionString"];

..но есть другая проблема с текстами данных LINQ-SQL - я думаю, они включают строки соединений в скомпилированную dll для использования в конструкторе без параметров. Как говорит Тванофоссон, мне нужно создать тексты данных, передав ссылку на строку подключения в web.config. Вот где я запутался :)

person flesh    schedule 25.10.2008
comment
Да, текст данных будет включать ConnectionString в качестве запасного варианта, если все другие способы найти ConnectionString с правильным именем не работают - вы всегда можете установить контекст с помощью конструктора, который принимает строку подключения в качестве параметра. - person Zhaph - Ben Duguid; 23.04.2009

Я тоже немного боролся с этой проблемой. Я нашел решение, используя определение частичного класса C # и расширив текст данных, созданный дизайнером dbml. Это решение очень похоже на ответ tvanfosson. Что вам нужно сделать, так это создать частичный класс datacontext с конструктором по умолчанию, получающим ConnectionString из настроек, а в свойствах DC конструктора dbml установить для подключения значение None. Таким образом, строка подключения не будет скомпилирована в dll. Datacontext автоматически получит строку подключения из настроек строки подключения web.config. Я не тестировал, работает ли это и с app.config, но я думаю, что он должен работать нормально.

Вот образец частичного класса постоянного тока:

namespace MyApplication {
    /// <summary>
    /// Summary description for MyDataContext
    /// </summary>
    /// 
    public partial class MyDataContext
    {
        public MyDataContext() :
            base(global::System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString, mappingSource)
        {
            OnCreated();
        }
    }
}
person Community    schedule 23.04.2009
comment
Не вызывает ли добавление этого конструктора проблемы компиляции, поскольку созданный дизайнером частичный класс уже имеет такой же конструктор? - person Frank Schwieterman; 15.06.2009
comment
Верно, в зависимости от выбранных вами свойств конструктора (раздел «Соединение») сгенерированный код может сам создать конструктор без параметров. - person Seba Illingworth; 15.08.2010
comment
Это мое предпочтительное решение. Установка соединения на None в конструкторе, как упоминал Hihu, предотвратит автоматическое создание конструктора без параметров, поэтому проблемы с компиляцией не возникнет. Аналогичное решение с более подробными деталями описано здесь. - person Mac; 09.06.2014

Ваше приложение будет использовать только записи конфигурации в файле 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>  
person TGnat    schedule 25.10.2008

Чтобы защитить его от чего-либо в автоматически сгенерированном коде, переопределите информацию о соединении в методе 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 может выполнять соединения по своему усмотрению (что неприятно за пределами веб-проекта), но вы получаете окончательный контроль над соединением при запуске приложения.

person Seba Illingworth    schedule 15.08.2010

Вот один из способов взглянуть на это. Какой компонент должен принять решение о том, какую базу данных использовать? Возможно, что база данных (или, по крайней мере, строка подключения) может измениться в будущем. Решает ли веб-сайт, какую базу данных использовать? Или решает DAL?

Если у вас есть базы данных dev, QA, UAT и prod, управление этими строками подключения имеет решающее значение.

Если веб-сайт решит, он должен передать строку подключения из своего файла web.config в DAL. Если веб-сайт не должен знать или заботиться о происхождении данных, тогда строка подключения принадлежит DAL.

person DOK    schedule 25.10.2008

Как насчет определения объекта ConnectionFactory, который принимает перечисление в качестве параметра и возвращает полностью сформированный объект соединения?

person endian    schedule 25.10.2008

Вы также можете попросить веб-приложение предоставить строку подключения, когда ему нужно использовать проект доступа к данным. Вы можете сделать его частью конструктора.

Кроме того, вы можете просто написать свою собственную логику для загрузки строки подключения из внешнего файла, когда проект доступа к данным вызывает ее.

person hugoware    schedule 25.10.2008

В идеальном мире, я думаю, вы бы реорганизовали свой уровень данных, чтобы получить параметры конфигурации через System.Configuration или соответствующие конструкторы / фабрики. Это означает, что вам нужно либо перенастроить его неявный источник конфигурации, либо явно установить соединения от его хоста / потребителя. Другой связанный шаблон для централизации этих типов констант - это добавить свойство readonly в статический вспомогательный класс и позволить этому классу управлять фактическим разрешением из конфигураций и т. Д.

Одно место, куда вы можете посмотреть, которое, как мне кажется, показывает хорошие примеры того, как это сделать элегантно, - это NHibernate и его управление конфигурацией / сопоставлениями. Конечно, это немного адский xml, и Fluent NHib более сладкий, но большинство реальных примеров покажут вам, как согласовать конфигурацию из вспомогательной сборки с выполняющейся сборкой.

person Grant    schedule 25.10.2008

Создайте собственный ConnectionFactory на основе файлов .config:

  • Определите настраиваемый раздел конфигурации для сопоставления пар ключ / строка подключения
  • Научите свой ConnectionFactory обнюхивать этот раздел конфигурации, используя имя хоста или имя машины в зависимости от ситуации.
  • Заполните значения ключа / строки подключения для различных серверов dev / qa / prod и поместите их в различные файлы app.config, web.config и т. Д.

Pro:

  • Все живет внутри проекта, так что никаких сюрпризов
  • Добавление дополнительной цели развертывания - это операция копирования / вставки в файл .config.

Против:

  • Подходит для больших уродливых XML-разделов, особенно если у вас дюжина производственных серверов.
  • Необходимо дублировать между проектами
  • Требуется изменение кода и повторное развертывание для добавления новой цели
  • Код должен знать об окружающей среде, в которой он будет жить.
person Jason Kester    schedule 25.10.2008

Я знаю, что это старый, но вот как я это делаю (мне очень нравится способ @Seba, но я этого не пробовал)

Предполагается, что ваш файл DBML находится в собственной библиотеке классов, что, по моему мнению, наиболее удобно при совместном использовании сущностей и доступе к данным на нескольких веб-сайтах и ​​других библиотеках классов. Также предполагается, что вы назвали строку подключения одинаково в каждом проекте. Я использую NAnt, чтобы установить это при развертывании в разных средах.

Я основывал это на верхнем ответе @tvanfosson выше - спасибо этому парню.

  1. Создайте свой собственный базовый класс, производный от 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
  1. Откройте файл DBML и в свойствах добавьте указанное выше имя класса к свойству базового класса.

Обратите внимание: если вы поместили класс контекста настраиваемых данных в ту же сборку, просто укажите имя класса, например CustomDataContext.

Если они находятся в разных сборках, используйте полное имя, например MyCo.MyApp.Data.CustomDataContext

  1. Чтобы убедиться, что конструктор работает правильно, скопируйте строку подключения в файл app.config для библиотеки классов. Он не будет использоваться отдельно от IDE.

Вот и все.

Вам нужно будет назвать свою строку подключения таким же

По сути, вы заставляете контекст данных игнорировать информацию о соединении, установленную в файле DBML. Использование методов ConfigurationManager будет означать, что он получит строку подключения из вызывающей сборки.

HTH

person Joe Niland    schedule 30.11.2010