ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Менеджер підключень до баз даних
         

     

    Інформатика, програмування

    Менеджер підключень до баз даних

    Андрій Майоров

    Введення

    Не буде великим перебільшенням сказати, що левова частка розробляються зараз програм використовує у своїй роботі бази даних. Більшість з них працює з однією базою, більш рідкісні представники - з двома, трьома, чотирма і так далі, аж до повної невизначеності в кількості баз на етапі розробки.

    Приклади додатків, що працюють з однією БД, знайомі кожному. З двома базами може працювати, наприклад, програма, синхронізуюча дані між ними. При цьому бази можуть мати різну структуру і управлятися різними СУБД. З заздалегідь невідомою кількістю баз може працювати, наприклад, додаток, що збирає статистику з багатьох джерел.

    У рамках цієї статті нас не дуже цікавить, що саме додаток робить з базою даних. Важливий сам факт з'єднання виконання яких би то не було операцій. Розглянемо традиційний для ADO.NET сценарій роботи з базою.

    По-перше, ми повинні знати рядок підключення (connection string), по-друге, створити об'єкт підключення відповідного типу, потім проініціалізувати його цим рядком, відкрити і почати використовувати. По завершенню використання об'єкт підключення слід знищити. Виглядає це приблизно так:        

    string conString = "...";   

    SqlConnection con = new SqlConnection ();   

    con.ConnectionString = conString;   

    using (con) (   

    con.Open ();   

    ...   

    )// В цій точці   підключення буде автоматично закрито і знищено     

    Код простий і логічний, але в реальному додатку з ним можуть виникнути проблеми:

    Майже завжди розробка програми ведеться не з тими рядками підключення, які будуть використовуватися при експлуатації. Відповідно, ми не можемо жорстко зашити рядок підключення в код, а повинні как-то ввести її під час роботи програми.

    У ряді програм розробник не знає конкретного типу об'єкта підключення і працює з базовим інтерфейсом IDbConnection. Для таких випадків код типу «new SqlConnection» не годиться.

    У той же час розробник зазвичай чітко уявляє, яке підключення йому потрібно відкрити, і може логічно позначити його «база А» або «база Б», що б це не означало в середовищі кінцевого користувача. У разі додатки з однією базою, можна підключатися і до деякої базі за замовчуванням, ніяк її не називаючи.

    Очевидно, що зручним рішенням могло б стати використання деякого механізму, що дозволяє отримувати об'єкт підключення до базі даних за його логічного імені. Кінцевому користувачеві цей механізм повинен надавати можливість швидко і просто асоціювати логічне назва з реальною рядком підключення. Наприклад, це можна робити у файлі конфігурації. Подібний механізм ми і назвемо «менеджером підключень до баз даних ».

    Основна функція менеджера

    Основна функція менеджера - по заданому логічному імені повернути об'єкт підключення потрібного типу, проініціалізувати потрібної рядком підключення.

    У використанні це може виглядати так:        

    SqlConnection   c1 = (SqlConnection) dbmgr [ "beta "];   

    IDbConnection c2 = dbmgr.Default;     

    Приведення типу в першому рядку обумовлено тим, що наш додаток може працювати з базами різних типів, і, отже, менеджер не може повертати об'єкт підключення якогось певного типу. Так як будь-який об'єкт підключення повинен реалізовувати інтерфейс IDbConnection, менеджеру найбільш логічно давати доступ до об'єктів саме через цей інтерфейс.

    Очевидно, що ця функція менеджера приблизно відповідає шаблону (патерни) проектування Factory Method.

    Перерахування підключень

    Ми вже говорили про додатки з невизначеним на етапі розробки кількістю підключень. У практиці застосування менеджера підключень це може виглядати, наприклад, так: програма повинна послідовно отримати інформацію з кожної БД, зареєстрованої в менеджері. При цьому на момент написання програми ми не знаємо, які бази, і в якому кількості будуть потрібні кінцевому користувачеві.

    Очевидно, що в цьому випадку логічні імена баз нас не дуже-то цікавлять. Набагато більше нам потрібна можливість перебору всіх баз в менеджері. Наприклад:        

    foreach (IDbConnection con in dbmgr) (   

    // Отримуємо інформацію   

    ...   

    )     

    Для того щоб ця мовна конструкція працювала, та й взагалі для перебору всіх наявних підключень, наш клас DbManager повинен реалізовувати інтерфейс IEnumerable.

    Конфігурування менеджера

    Налаштування менеджера полягає в установці відповідності між логічним ім'ям підключення та інформацією, достатньою для створення об'єкта підключення. Нескладні міркування показують, що достатньо знати тип об'єкта підключення, рядок підключення, а також чи є дане підключення підключенням за замовчуванням.

    Як вже вказувалося, простіше за все це налаштування робити через конфігураційний файл програми. При цьому і у розробника є єдине місце для опису підключень, і кінцевий користувач може швидко адаптувати додаток до своїх умов. Потрібно відзначити, втім, що при цьому підході кінцевий користувач повинен знати стандартний формат строк підключення ADO.NET.

    Примірний код, інструктують менеджера призвести читання настроювальних даних, навіть якщо він вже був налаштований:        

    dbmgr.Configure (   true);// forceReload = true     

    При цьому формат секції конфігураційного файлу може бути таким:        

      

      

    connectionString ="..."      

    default = "true" />   

      

    connectionString ="..."   

    type = "OleDbConnection" />   

        

    Тут декларується, що додаток використовує два бази даних. Перша з них називається alfa, обслуговується об'єктом типу SqlConnection (бо нічого іншого не вказано), і є підключенням по замовчуванням. Друга носить логічне ім'я beta і обслуговується об'єктом типу OleDbConnection. Безумовно, для обох баз зазначені і коректні рядка підключень.

    Маючи простий і зручний спосіб опису підключень через конфігураційний файл, ми, тим не менш, не повинні забувати, що бувають ситуації, коли все це повинно бути зроблено програмним шляхом. Наприклад, так:        

    Configuration config = new Configuration ();   

    // Налаштовуємо об'єкт config   

    ...   

    // Призначаємо конфігурацію менеджеру   

    DbManager.Configure (config);     

    У даному випадку об'єкт типу Configuration надає нам ті ж можливості налаштування, що й файл конфігурації.

    Дуже важко уявити додаток, в якому існувало б кілька окремих наборів підключень до баз даних. Я кажу, наприклад, про ситуацію, коли у двох різних місцях програми ми використовуємо два різних підключення з ім'ям beta. Які висновки з цього випливають?

    По-перше, це означає, що всі екземпляри менеджера підключень, що використовуються в програмі, повинні бути налаштовані однаково. Відповідно, методи Configure (...) ми сміливо можемо робити статичними.

    По-друге, напрошується висновок, що ми цілком можемо обійтися одним екземпляром менеджера на всі додаток. У деяких випадках, про які ми поговоримо пізніше, нам знадобиться більше, але все ж таки обмежений кількість екземплярів. З цього випливає, що примірник менеджера ми повинні отримувати не за допомогою оператора new, а за допомогою якогось статичного методу класу. Приклад:        

    DbManager dbmgr = DbManager.Get ();     

    Подібний підхід нагадує про паттерні проектування Singleton, але, на відміну від класичної трактування, у нас може бути не один екземпляр, а декілька. Втім, про це ми ще поговоримо.

    Структура класу

    продуманий сценарій використання менеджера, ми можемо спроектувати структуру класу. Ось вона:        

    public class DbManager: IEnumerable   

    (   

    public static DbManager Get ()   {...}      

    public IDbConnection   this [string name]   

    (   

    get {...}   

    )      

    public IDbConnection Default   

    (   

    get {...}   

    )      

    public static void Configure (   bool forceReload) {...}      

    public static void Configure (   Configuration config) {...}      

    public IEnumerator   GetEnumerator () {...}      

    // Непублічні методи і члени класу   

    ...   

    )     

    Короткий опис методів:

    Get - повертає менеджер підключень. Якщо примірники менеджера ще немає, створюється новий.

    this [string] - повертає об'єкт підключення по даного логічного імені. У тому випадку, якщо ім'я не вказано (так само null), повертається об'єкт підключення за замовчуванням.

    Default - повертає об'єкт підключення за замовчуванням.

    Configure (bool) - читає конфігураційні інформацію з конфігураційного файлу. Якщо ми намагаємося працювати з ще не сконфігурованих менеджером, він повинен автоматично викликати цей метод.

    Configure (Configuration) - налаштовує менеджер в Відповідно до даного конфігураційних об'єктом.

    GetEnumerator - дозволяє пробігтися по всіх підключень менеджера циклом foreach.

    Варіанти роботи з базою

    Ми вже розглядали шматок типового коду, що працює з базою. Більш повний фрагмент виглядає так: ми спочатку створюємо підключення (наприклад, SqlConnection), потім створюємо команду (SqlCommand), додаємо до команді параметри, асоціюємо її з підключенням, відкриваємо підключення, виконуємо команду, закриваємо підключення:        

    SqlConnection con = new SqlConnection ();   

    con.ConnectionString = "...";   

    SqlCommand cmd = new SqlCommand ();   

    cmd.CommandText = "...";   

    cmd.Connection = con;   

    cmd.Parameters.Add (new SqlParameter (...));   

    using (con) (   

    con.Open ();   

    cmd.Execute ();   

    ...      

    )     

    Ми робимо це при кожному зверненні до бази, так що виникає питання: а чи не буде швидше заздалегідь створити і зберегти підключення і команду, а потім тільки їх використати? З точки зору елементарної логіки здається очевидним, що має бути швидше. З іншого боку, відомо, що створення об'єктів будівництва. NET Framework відбувається дуже швидко, тому що виграш навряд чи буде великим.

    Проведемо тест. В одному прогоні ми будемо кожен раз створювати підключення і команду, а в іншому - використовувати готові об'єкти. Команді визначимо три параметри. У двох прогонах по 100 000 ітерацій вдалося з'ясувати наступне:

    Перший підхід, при якому все створюється заново, приблизно на 5 відсотків повільніше друга.

    В абсолютному вирахуванні це уповільнення складає всього 0.08 мілісекунди на кожну ітерацію, тобто дуже мало. Якщо врахувати, що саме звернення до бази виконується на кілька порядків повільніше створення будь-якого об'єкта, то виграш виходить і зовсім умоглядний.

    Які висновки? По-перше, логіка перемогла - не створювати об'єкти виявилося швидше, ніж створювати. По-друге, це зовсім не важливо. Різниця в швидкості між пересозданіем об'єктів і використанням готових настільки мала, що розробник може сміливо вибирати той чи інший підхід, керуючись тільки своїм особистим розумінням прекрасного.

    Говорячи ж про практичну економії, можна зробити таку оцінку: якщо у нас є якась динамічна web-сторінка, яка робить одне звернення до бази, а до неї самої звертаються 10 раз в секунду, то збереження об'єктів допоможе нам виграти цілу секунду за півгодини.

    Природно, між цими двома полярними варіантами є велике число проміжних станів. Наприклад, можна щоразу створювати об'єкт підключення, але зберігати готові команди. Цей підхід, ймовірно, дуже органічно поєднується з візуальним дизайнером компонентів з Visual Studio. Накидавши на компонент команди, ми отримуємо код для їх ініціалізації, який виконується при створенні екземпляра компонента. Очевидно, що витягати цей код з методу InitializeComponent нерозумно, краще просто призначити потрібної команді той об'єкт підключення, що ми збираємося відкривати в даний момент.

    Повторне використання підключень

    У той час як з повторним використанням командних об'єктів (SqlCommand, OleDbCommand і т.п.) все, загалом, зрозуміло, питання повторного використання об'єкта підключення залишається відкритим. Чи потрібно це кому-небудь, а якщо потрібно, то навіщо?

    Під «повторним» ми тут розуміємо таке використання, коли один і той самий об'єкт підключення використовується знову і знову в усіх частинах програми, де потрібен доступ до відповідної бази даних. При цьому ми усвідомлюємо, що всі стандартні для ASP.NET об'єкти підключення не є безпечними для багатопотокового використання (non thread safe), тому для початку будемо вважати, що наша програма має тільки один потік.

    Які проблеми можуть виникнути при подібному використання об'єкта підключення? По-перше, кожного разу, відкриваючи підключення до бази даних, можна виявити, що воно вже було відкрито раніше. Спроба відкриття вже відкритого підключення викликає помилку. По-друге, відкритий об'єкт DataReader блокує своє підключення, так що до його закриття виконати ще будь-яку команду неможливо. Це може створити проблему в методі, викликаному під час читання даних з бази.

    Першу проблему обійти нескладно. Досить перевіряти стан підключення перед відкриттям, і пропускати цей крок, якщо воно вже відкрито. Тут важливо помітити, що метод, який відкрив підключення, обов'язково повинен його закрити, так що, якщо перевірка показала, що підключення потрібно відкривати, це означає і те, що його потрібно закрити після використання.

    Обійти другу проблему в тому місці, де вона дала про себе знати, неможливо. Дійсно, дані вже читаються, підключення вже заблоковано, і зробити ми з цим нічого не можемо. Єдине рішення тут - Так проектувати блоки читання, щоб вони навіть потенційно не могли нікого блокувати. Наприклад, можна спочатку прочитати всі дані в масив, а вже потім проводити їх подальшу обробку.

    Основні проблеми ясні і досить серйозні. Які переваги можуть бути у даного підходу? Переважують вони недоліки?

    По-перше, ми можемо істотно прискорити роботу в тих додатках, де свежеоткритое підключення потрібно спеціально готувати. Наприклад, додаток може використовувати application roles. Щоб увійти в роль MS SQL Server вимагає виконання збереженої процедури sp_setapprole:        

    EXEC   sp_setapprole 'SalesApprole', 'AsDeFXX'     

    Очевидно, що якщо обробка запиту полягає, до Наприклад, з п'яти звернень до бази, то набагато швидше буде відкрити підключення і виконати цю команду один раз, ніж усі п'ять. Сама операція відкриття підключення вимагає дуже мало часу - на це є connection pooling. Зайве ж звернення до бази - це серйозний удар по швидкодії.

    Природно, я говорю не про простому випадку, коли всі п'ять звернень до бази знаходяться в одному методі. Врешті-решт, ми живемо у часи переміг об'єктно-орієнтованого підходу, тому що «макаронний» код майже спочив у бозі. Всі ці звернення здійснюються різними компонентами, обслуговуючими запит. Як бути в цьому випадку? Пропонується відкрити підключення і виконати цю команду на початку обробки запиту, а потім передати об'єкт підключення у подальше використання.

    Видається, що ця перевага виглядає досить серйозним (звісно, для певного класу додатків). До речі, тут варто звернути увагу ще на одну особливість: увійшовши в роль і розраховуючи на автоматичний вихід з неї із закриття підключення, можна отримати неприємний сюрприз у тому випадку, якщо підключення на самому справі закрито не буде. Подальші звернення до БД, можливо, будуть виконуватися з невідповідними правами. З іншого боку, це може статися тільки в додатку з вельми специфічною архітектурою.

    По-друге, можна уявити собі додаток, що відкриває надто багато підключень. Величезна вкладеність дзвінків. Може Можливо, навіть рекурсія. Всі методи відкривають підключення і, не закриваючи, викликають інші методи. У такому (абсолютно гіпотетичний) додатку можна зіткнутися з тим, що вільні підключення закінчаться, і в якийсь момент часу ми не зможемо відкрити підключення до бази. Використання одного об'єкта підключення могло б нас тут врятувати.

    Втім, подібна проблема виглядає абсолютно надуманою. Якщо вона і має де-те місце, то це, швидше, помилка в проектуванні програми, і вирішувати її потрібно іншими способами.

    Режими функціонування менеджера

    Повернемося до менеджера підключень. Очевидно, що він міг б функціонувати в двох режимах: або кожного разу створювати новий об'єкт підключення, або повертати вже готовий екземпляр, що відповідає заданому логічного імені.

    На практиці менеджер, який здійснює кешування об'єктів підключення, повинен успішно проходити ось такий тест:        

    DbManager.Mode   = DbManagerMode.CacheConnections;   

    DbManager   dbmgr = DbManager.Get ();   

    IDbConnection   c1 = dbmgr [ "beta "];   

    IDbConnection   c2 = dbmgr ["beta "];   

    Assert.IsTrue (c1 == c2);// Менеджер   повертає один і той же екземпляр     

    А «простий» менеджер, тобто не здійснюють кешування, такий:        

    DbManager.Mode   = DbManagerMode.DoNotCacheConnections;   

    DbManager   dbmgr = DbManager.Get ();   

    IDbConnection   c1 = dbmgr [ "beta "];   

    IDbConnection   c2 = dbmgr [ "beta "];   

    Assert.IsTrue (c1! = c2);// Менеджер   повертає різні екземпляри     

    Подібна функціональність дозволила б розробнику легко вибирати між двома описаними вище варіантами роботи з з'єднаннями, і навіть без великих складнощів перейти з одного підходу на інший у вже частково написаному додатку.

    Нить

    Раніше ми розглядали додаток, в якому є тільки один потік (thread). Які складнощі можуть зустрітися, якщо потоків буде декілька?

    По суті, складність тут тільки один - один об'єкт підключення можна одночасно використовувати тільки в одному потоці. Якщо для кожного звернення створюється новий об'єкт підключення, то нас ця проблема абсолютно не стосується. Відповідно, менеджер підключень, що працює в цьому режимі, дуже простий у реалізації. Метод, який повертає об'єкт підключення по імені, має виглядати так:        

    public override IDbConnection this [String name]   

    (   

    get   

    (   

    ConnectionInfo info =   (ConnectionInfo) _config.Connections [name];   

    if (info! = null)   

    (   

    return CreateConnection (info   );   

    )   

    return null;   

    )   

    )     

    Кілька складніше виглядають дії при кешуванні об'єктів підключення. З одного боку, потрібно організувати якийсь словник готових об'єктів, з якого видаються об'єкти за запитом. З іншого боку, ми повинні зробити так, щоб кожен потік працював зі своїм екземпляром даного підключення. Намагатися реалізувати таку функціональність в одному об'єкті, що обслуговує відразу всі потоки, може бути дуже складно. Тому пропонується зробити так, щоб кожен екземпляр менеджера обслуговував тільки один потік. Очевидно, що в цьому випадку створюється якийсь невизначений кількість примірників, не більше, ніж загальна кількість потоків у додатку. Невизначеність обумовлена тим, що для потоків, в яких менеджер не потрібно, створювати екземпляр не потрібно.

    Таким чином, потрібно, по-перше, написати такий метод Get, який би повертав екземпляр, приписаний до викликає потоку, і, по-друге, зробити словник готових об'єктів. Приблизно так може виглядати цей фрагмент коду:        

    [ThreadStatic] private static DbManager _instance;   

    private ListDictionary _connections = new ListDictionary ();      

    internal static new DbManager Get ()   

    (   

    // Якщо екземпляр вже є, повернути його   

    if (_instance! = null) return _instance;      

    // Створити новий екземпляр   

    _instance = new CachingDbManager ();   

    _instance.Init ();   

    return _instance;   

    )      

    public override IDbConnection this [String name]   

    (   

    get   

    (   

    // Намагаємося взяти готовий об'єкт зі словника   

    IDbConnection result =   (IDbConnection) _connections [name];   

    if (result == null)   

    (   

    // Шукаємо опис підключення в   конфігурації   

    ConnectionInfo info =   (ConnectionInfo) _config.Connections [name];   

    if (info! = null)   

    result = CreateConnection (   info);   

    )   

    return result;   

    )   

    )     

    Менеджер і ASP.NET

    Як відомо, додатки ASP.NET досить активно використовують багатопоточність. У той же час роблять вони це настільки неявно, що цей факт легко залишити без уваги і отримати несподівані помилки.

    Згадаймо, у загальних рисах, структуру звичайного додатки ASP.NET. У домені додатку (AppDomain) є кілька примірників класу HttpApplication. Кожен з цих примірників має набір супутніх йому модулів (HttpModule). Набір модулів у кожного програми однаковий, та й самі програми, по ідеї, не повинні нічим відрізнятися. Далі, домен програми має набір робочих потоків (working threads), готових обслуговувати запити користувачів. З усією очевидністю, потоків існує принаймні стільки ж, скільки об'єктів Http-додатки.

    При обслуговуванні запиту ASP.NET якимсь псевдовипадкових чином вибирає робочий потік і об'єкт додатка, з яким цей потік буде працювати. У зв'язку з цим, певний об'єкт програми в різних запитах буде, швидше за все, працювати з різними потоками.

    Припустимо тепер, що в нашому додатку ми створили командний об'єкт (SqlCommand) і зберегли його для подальшого використання. Команда пов'язана з певним об'єктом підключення, а саме, з тим об'єктом, який був повернутий менеджером підключень у момент створення і перший виконання команди. Не будемо, однак, забувати, що даний об'єкт HttpApplication при обслуговуванні наступного (у його хронології) запиту, швидше за все, буде працювати вже з іншим робочим потоком, а тому менеджер підключень поверне не то підключення, з яким пов'язана наша команда. Гірше того, з повернутих підключенням, ймовірно, буде пов'язана аналогічна команда в іншому об'єкті додатки.

    Вихід з описаної ситуації досить простий. Необхідно зробити такий модуль (HttpModule), який на початку обробки запиту буде пов'язувати менеджер підключень, приписаний до даного об'єкта додатки, з потоком, який зараз працює з цієї програми та з усіма підлеглими йому об'єктами. Це усуне всі проблеми такого роду і дозволить знову забути про реальний стан справ з потоками в ASP.NET.

    Код модуля гранично простий:        

    public class AspAdapter: IHttpModule   

    (   

    private HttpApplication   application;   

    private DbManager manager;      

    public void   Init (System.Web.HttpApplication context)   

    (   

    application = context;   

    manager = DbManager.Get ();   

    application.BeginRequest + =   new EventHandler (OnBeginRequest);   

    )      

    protected void OnBeginRequest (   object sender, EventArgs e)   

    (   

    manager.Init ();   

    )      

    public void Dispose ()   

    (   

    application.BeginRequest -=   new EventHandler (OnBeginRequest);   

    )   

    )     

    В останніх прикладах можна помітити раніше не згадуваний метод Init. Він служить для прив'язки даного екземпляра менеджера до викликає потоку.

    Будь ласка, закривайте двері!

    Загальновідомо, що при використанні пулу підключень (connection pool) основний принцип роботи з з'єднаннями говорить: відкривай пізно, закривай рано. Іншими словами, потрібно відкривати перед самим використанням, а закривати відразу після оного. При цьому потрібно пам'ятати, що в блоці використання підключення до бази може статися якась помилка (exception), яка завадить закрити підключення.

    Ми вже розглядали звичайний для ADO.NET спосіб роботи з об'єктом підключення, приблизно такий:        

    using (connection) (   

    connection.Open ();   

    // Активніше використовуємо підключення! Інакше,   навіщо відкривали?!   

    ...   

    )     

    Він простий і вдалий, якщо не шкода знищити об'єкт підключення в кінці блоку, тобто якщо кожного разу створюється новий об'єкт підключення. Якщо ж хочеться використовувати один об'єкт, код стає набагато менш красивим:        

    bool wasOpened = false;   

    if (connection.State == ConnectionState.Closed)   

    (   

    connection.Open ();   

    wasOpened = true;   

    )   

    try (   

    // Використовуємо підключення   

    ...   

    )   

    finally   

    (   

    if (wasOpened)   connection.Close ();   

    )     

    Крім цілому, він ще й ускладнює можливість перемикання між двома режимами роботи менеджера підключень. Ми ж не хочемо, перекинувши прапор, ще й ред всі блоки роботи з базою.

    Використовуємо для вирішення той же механізм детермінованою деструкції - інтерфейс IDisposable, який настільки спрощує використання підключення за стандартною схемою. Нам достатньо створити клас, який при конструюванні відкривав би підключення, якщо це необхідно, а при знищення - закривав би, коли відкривав його сам. При цьому ми можемо зробити клас досить розумним, щоб він розумів, в якому режимі працює менеджер підключень. Якщо менеджер не кешує об'єкти, наш сервісний клас буде видаляти їх в кінці блоку using.

    У використанні це буде виглядати приблизно так:        

    using (new DbOpen (connection   )) (   

    //   Використовуємо підключення, раз відкривали!   

    ...   

    )     

    Відзначимо також, що при конструюванні примірника класу підключення відкривається автоматично. Таким чином, ще один рядок у стандартному сценарії стає непотрібною.

    Реалізація

    Даний підхід уже реалізований і неодноразово пройшов польові випробування. Більш докладно про готовому модулі можна дізнатися на http://www.byte-force.com/russian/products/tech/lsddatabase.html.

    Посилання

    E. Gamma, et al., Design Pattern: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995        

    ПРИМІТКА   

    Від редакції   

    Багато учасників редакційної колегії,   мають досвід роботи з базами даних, неоднозначно розцінює цю статтю.   З одного боку, ідея інкапсуляції роботи з підключенням, що дозволяє   отримувати підключення по логічним іменах, хороша. Вона спрощує код, тим   самим знижуючи ймовірність появи помилок. З іншого боку, створення   саморобного пулу, а також реалізація закриття з'єднання і многопоточной   роботи, є успішним рішенням власноруч створеної проблеми. Більше   того, так як кеш може повертати один і той же екземпляр підключення при   різних викликах, що в програмі може виникнути помилка з-за випадкового   (неявного) використання одного підключення в різних алгоритмах. Тобто   велика ймовірність того, що програміст у двох алгоритмах спробує створити   два незалежних транзакції, але оскільки підключення фізично одне, це йому   не вдасться. Як, власне, зауважив сам автор, виграшу в швидкості таке   рішення не дає, і сенс кешування просто незрозумілий.   

    Таким чином, ми рекомендуємо   використовувати на практиці ідею інкапсуляції роботи з з'єднаннями, але не   кешування підключень. Однак вивчення цієї частини статті цікаво, тому що   в ній використовуються методи оптимізації, цілком застосовні в інших випадках.     

    Список літератури

    Для підготовки даної роботи були використані матеріали з сайту http://www.rsdn.ru/

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status