Сохранение независимости базы данных приложения (ADO.NET против инкапсуляции логики БД)

Мы делаем довольно серьезное приложение, которое должно оставаться независимым от БД, которую хочет использовать клиент. Изначально мы планируем поддерживать MySQL, Oracle и SQL Server. Таблицы и представления просты, как и запросы (без реального причудливого SQL), поэтому вопрос:

  1. Используйте собственные драйверы БД (MySQLDbConnection и т. д.) и инкапсулируйте логику выполнения запросов и обработки результатов или
  2. Используйте общий OleDbConnection

Очевидно, что вариант 2 не требует накладных расходов, но я предполагаю, что производительность не так высока, как при собственном доступе?


person Andrew White    schedule 20.06.2010    source источник
comment
Стоит отметить, что если вы используете ADO.NET: Microsoft планирует прекратить поддержку своего поставщика данных Oracle (System.Data.OracleClient) см. здесь   -  person LauraB    schedule 24.06.2010
comment
Также см. stackoverflow. ком/вопросы/13133804/   -  person nawfal    schedule 20.02.2013


Ответы (8)


Примечание. Этот ответ актуален, если вы решили использовать базовые функции ADO.NET 2 вместо ORM (например, Entity Framework или NHibernate) или LINQ to SQL.

Предположим, у вас есть строка подключения, определенная в вашем app.config:

<connectionStrings>
    <add name="SomeConnection"
         providerName="System.Data.SqlClient"
         connectionString="..." />
</connectionStrings>

Обратите внимание на наличие атрибута providerName и его значения. Вы также можете указать значение для другого поставщика БД, например. System.Data.SQLite.

(Обратите внимание, что нестандартные поставщики, т. е. те, которые не входят в .NET Framework по умолчанию, нужно сначала зарегистрироваться либо в app.config, либо в клиентской машине machine.config.)

Теперь вы можете работать с указанной базой данных полностью независимо от поставщика следующим образом:

using System.Configuration;  // for ConfigurationManager
using System.Data;           // for all interface types
using System.Data.Common;    // for DbProviderFactories

var cs = ConfigurationManager.ConnectionStrings["SomeConnection"];
//                                              ^^^^^^^^^^^^^^^^

var factory = DbProviderFactories.GetFactory(cs.ProviderName);
//                                           ^^^^^^^^^^^^^^^

using (IDbConnection connection = factory.CreateConnection())
{
    connection.ConnectionString = cs.ConnectionString;
    //                            ^^^^^^^^^^^^^^^^^^^
    connection.Open();
    try
    {
        using (IDbCommand command = connection.CreateCommand())
        {
            ...  // do something with the database
        }
    }
    finally
    {
        connection.Close();
    }
}

Обратите внимание, что этот код работает только с типами интерфейса. Единственное место, где вы указываете конкретного поставщика БД, — это значение атрибута providerName в файле app.config. (Я отметил все места, где берется настройка из app.config, с помощью ^^^s.)


Дальнейшее чтение:

person stakx - no longer contributing    schedule 20.06.2010
comment
P.S.: Этот ответ не распространяется на параметризованные команды. .NET Framework не предоставляет независимый от поставщика БД способ обращения к параметрам команды в SQL CommandText. - person stakx - no longer contributing; 01.10.2011
comment
У меня был следующий подход к работе с SQL Server и Oracle в одном приложении: DbParameter p = factory.CreateParameter(); p.ParameterName = toDateTime; p.DbType = DbType.Date; p.Value = toDateTime; команда.Параметры.Добавить(p); - person Thomas Bratt; 13.06.2012
comment
@ Томас, вы правы, но как тогда вы будете ссылаться на параметр в фактической команде (IDbCommand.CommandText)? Должно ли это быть @toDateTime (именованный параметр)? Или вы используете токен ? (позиционный параметр)? Или какой-то другой синтаксис? ADO.NET на самом деле не предлагает независимого от поставщика решения для этого. - person stakx - no longer contributing; 13.06.2012
comment
Хорошая точка зрения. Когда я делал это раньше (для Oracle, SQLite и SQL Server), я заменял строку в тексте запроса, преобразовывая ':' в '@' для SQL Server. Это не элегантно, но работает. К сожалению, он становится неуправляемым из-за других различий в синтаксисе SQL, поэтому SQL должен поддерживаться на самом низком уровне общего знаменателя. Лучше всего использовать ORM, очевидно :) - person Thomas Bratt; 14.06.2012
comment
... или задаться вопросом, действительно нужен ли вам ваш код, чтобы он был независимым от БД/агностическим. Как часто вы на самом деле переносите производственную БД в другую СУБД? Возможно, не очень часто. - person stakx - no longer contributing; 14.06.2012

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

person Darin Dimitrov    schedule 20.06.2010
comment
В чем-то согласен, но у ORM есть свои проблемы, начиная с производительности. - person Cylon Cat; 21.06.2010
comment
@Cylon Cat, в больших приложениях, как здесь, кажется, потеря производительности, вызванная ORM, действительно мала по сравнению с другими частями и перевешивает такие преимущества, как наличие единого языка для выражения всего, ремонтопригодность, агностицизм базы данных, заполнение пробела между реляционным и объектно-ориентированным миром, .... - person Darin Dimitrov; 21.06.2010
comment
в качестве обобщения я согласен, но, как и во всем, снижение производительности не распространяется равномерно на приложение. Некоторые функциональные возможности могут быть особенно сложными или особенно чувствительными к производительности, и это помогает точно знать, как и где ORM может столкнуться с проблемами. Здесь очень полезны прототипы, базовое тестирование производительности и профилирование. - person Cylon Cat; 21.06.2010

Вам не нужно OleDbConnection для доступа к неспецифическим поставщикам ADO.NET. Просто используйте DbConnection et. др. См. DbProviderFactories в MSDN для получения дополнительной информации.

person Stephen Cleary    schedule 20.06.2010

Включив Oracle в этот список, вы гарантировали, что все будет просто.

  • Oracle использует другой символ префикса (двоеточие) для параметров по сравнению с SQL Server, который использует символ "at".
  • Oracle использует один тип данных (число) для long, int, short, boolean, float и decimal; ваш код должен быть уверен, что вы сопоставляете их правильно.
  • Вы должны параметризовать значения даты и времени Oracle; если вы попытаетесь использовать строки для дат в своих операторах SQL, вы сойдете с ума из-за формата даты Oracle. (Oracle использует трехсимвольную аббревиатуру месяца в формате 01-JAN-2010.)
  • Базовые функции SQL для обработки пустых значений могут быть разными, особенно для объединения нулевых значений. («NVL» по сравнению с «COALESCE») Oracle гораздо более требователен к зарезервированным словам.
  • Oracle не имеет встроенной поддержки столбцов идентификаторов. Обходные пути включают последовательности, триггеры и требование транзакций только для извлечения значения идентификатора из новой строки.

Другими словами, ваше приложение не может быть независимым от БД. Если вы не используете ORM, вам обязательно нужно создать уровень доступа к данным, который скроет все эти вещи от остальной части приложения.

Голос опыта здесь. Просто говорю'. Для общей схемы для SQL Server и Oracle нам пришлось построить большую часть инфраструктуры ORM, избегая при этом аспектов, которые могут снизить производительность. Интересно, но нетривиально, однозначно!

person Cylon Cat    schedule 20.06.2010
comment
Oracle не использует трехсимвольную аббревиатуру месяца. Дата/время внутренне поддерживаются в виде семи байтов. Когда вы конвертируете в символьный формат, это будет маска формата по умолчанию, если вы ее не укажете. Вы можете установить для этой маски формата по умолчанию все, что захотите. - person Gary Myers; 21.06.2010
comment
PS Oracle поддерживает COALESCE. Самая большая разница между Oracle и другими поставщиками СУБД для значений NULL заключается в том, что Oracle не поддерживает ненулевые строки нулевой длины. ППС. Проголосовал за, потому что я согласен с тем, что агностик БД - это непросто - person Gary Myers; 21.06.2010
comment
@ Гэри, приятно знать, спасибо. Эта часть миграции уже была выполнена, когда я начал проект. - person Cylon Cat; 21.06.2010

LINQ — высоко оцененный .NET ORM, отчасти потому, что вы можете использовать его и хранимые процедуры. Проблема в том, что это только SQL Server, но люди работают над тем, чтобы обеспечить аналогичную функциональность для Oracle и MySQL.

Что касается оптимизации базы данных и запросов, я съеживаюсь при мысли об использовании ORM. Типы данных, функции и общий синтаксис не очень переносимы в SQL. Наиболее эффективным средством взаимодействия с каждой базой данных будет адаптация модели и запросов к каждой из них, но это требует опыта, времени и денег. При необходимости сосредоточьтесь на одном поставщике базы данных с настройкой кода для поддержки замены поставщика и добавьте поддержку других баз данных по мере необходимости.

person OMG Ponies    schedule 20.06.2010
comment
LINQ to Entities может поддерживать другие базы данных. Я полагаю, что он может поддерживать любого поставщика данных ADO.NET во время выполнения, и единственными ограничениями для поставщиков, отличных от SQLServer, будут во время разработки. Я с удовольствием работаю с ним над SQLite; у него есть только пара особенностей времени разработки. - person Stephen Cleary; 21.06.2010

Нет веских причин избегать наиболее универсальных интерфейсов с самой широкой поддержкой — OleDb и даже ODBC, если они вас устраивают. Все, что выходит за рамки этого, сокращает количество продуктов/языков/платформ/инструментов/разработчиков, с которыми вы можете работать. Будучи ближе всего к металлу SQL, поставщик не собирается привносить большую неэффективность - определенно меньше, чем более эзотерические варианты. Они были вокруг долгое, долгое время, чтобы отжать любые проблемы.

Если вы собираетесь добавить уровень абстракции (своего или чужого), то это следует решать на основе достоинств абстракций, представленных в вашем конкретном контексте, а не просто иметь слой абстракции (который просто обеспечивает дополнительную поддержку, если только есть преднамеренная выгода.)

Как видите, пробег у всех разный. :) А вообще, я думаю чем проще, тем лучше.

person dkretz    schedule 20.06.2010

Почему бы не использовать Patterns & Practices корпоративную библиотеку Блок приложений для доступа к данным. Накладные расходы минимальны, а смена провайдера выполняется очень быстро.

Цитировать:

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

person Laramie    schedule 20.06.2010

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

Таким образом, вы получаете развязку при использовании DAL, но пользу от улучшений производительности или специфичных для поставщика конструкций в DAL.

person Adam Houldsworth    schedule 24.06.2010