Каков наилучший способ поддержки нескольких баз данных для .NET-продукта?

Мы разрабатываем продукт, который может поддерживать несколько баз данных. В настоящее время мы делаем что-то подобное, чтобы наш код поддерживал MS SQL, а также MySQL:

namespace Handlers
{
    public class BaseHandler
    {
        protected string connectionString;
        protected string providerName;

        protected BaseHandler()
        {
            connectionString = ApplicationConstants.DatabaseVariables.GetConnectionString();
            providerName = ApplicationConstants.DatabaseVariables.GetProviderName();
        }
    }
}

namespace Constants
{
    internal class ApplicationConstants
    {
        public class DatabaseVariables
        {
            public static readonly string SqlServerProvider = "System.Data.SqlClient";
            public static readonly string MySqlProvider = "MySql.Data.MySqlClient";

            public static string GetConnectionString()
            {
                return ConfigurationManager.ConnectionStrings["CONNECTION_STRING"].ConnectionString; 
            }

            public static string GetProviderName()
            {
                return ConfigurationManager.ConnectionStrings["CONNECTION_STRING"].ProviderName;
            }
        }
    }
}

namespace Handlers
{
    internal class InfoHandler : BaseHandler
    {
        public InfoHandler() : base()
        {
        }

        public void Insert(InfoModel infoModel)
        {
            CommonUtilities commonUtilities = new CommonUtilities();
            string cmdInsert = InfoQueryHelper.InsertQuery(providerName);
            DbCommand cmd = null;
            try
            {
                DbProviderFactory provider = DbProviderFactories.GetFactory(providerName);
                DbConnection con = LicDbConnectionScope.Current.GetOpenConnection(provider, connectionString);
                cmd = commonUtilities.GetCommand(provider, con, cmdInsert);
                commonUtilities.PrepareCommand(cmd, infoModel.AccessKey, "paramAccessKey", DbType.String, false, provider, providerName);
                commonUtilities.PrepareCommand(cmd, infoModel.AccessValue, "paramAccessValue", DbType.String, false, provider, providerName);
                cmd.ExecuteNonQuery();
            }
            catch (SqlException dbException)
            {
                //-2146232060 for MS SQL Server
                //-2147467259 for MY SQL Server
                /*Check if Sql server instance is running or not*/
                if (dbException.ErrorCode == -2146232060 || dbException.ErrorCode == -2147467259)
                {
                    throw new BusinessException("ER0008");
                }
                else
                {
                    throw new BusinessException("GENERIC_EXCEPTION_ERROR");
                }
            }
            catch (Exception generalException)
            {
                throw generalException;
            }
            finally
            {
                cmd.Dispose();
            }
        }
    }
}

namespace QueryHelpers
{
    internal class InfoQueryHelper
    {
        public static string InsertQuery(string providerName)
        {
            if (providerName == ApplicationConstants.DatabaseVariables.SqlServerProvider)
            {
                return @"INSERT INTO table1
           (ACCESS_KEY
           ,ACCESS_VALUE)
     VALUES
           (@paramAccessKey
           ,@paramAccessValue) ";
            }
            else if (providerName == ApplicationConstants.DatabaseVariables.MySqlProvider)
            {
                return @"INSERT INTO table1
           (ACCESS_KEY
           ,ACCESS_VALUE)
     VALUES
           (?paramAccessKey
           ,?paramAccessValue) ";
            }
            else
            {
                return string.Empty;
            }
        }
    }
}

Можете ли вы предложить, если есть лучший способ сделать это? Каковы же плюсы и минусы подхода?

Ответ 1

Для вашей текущей потребности я согласен с NHibernate...

Просто хочу указать что-то с вашей иерархией классов...

Вам лучше использовать интерфейс

Как (просто проверьте документ или Интернет для точного синтаксиса)

Interface IDBParser  
    Function1  
    Function2  

class MSSQLParser : IDBParser  
    Function1  
    Function2  

class MySQLParser : IDBParser  
    Function1  
    Function2 

Затем в вашем коде вы можете использовать интерфейс

Main()  
    IDBParser dbParser;  
    if(...)  
       dbParser = new MSSQLParser();  
    else  
       dbParser = new MySQLParser();  

    SomeFunction( dbParser );  

// the parser can be sent by parameter, global setting, central module, ...  
    SomeFunction( IDBParser dbParser)  
      dbParser.Function1();  

Таким образом, управление будет проще, и ваш код не будет заполнен одним и тем же условием if/else. Также будет намного проще добавить другие DB. Другим преимуществом является то, что он может помочь вам с модульным тестированием, отправив макет объекта.

Ответ 2

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

Без сомнения, вы должны использовать NHibernate. Его объектно-реляционный сопоставитель, который делает доступ к базе данных прозрачным: вы определяете набор классов DAL, которые представляют каждую таблицу в вашей базе данных, и вы используете поставщиков NHibernate для выполнения запросов к вашей базе данных. NHibernate будет динамически генерировать SQL, необходимый для запроса базы данных и заполнения ваших DAL-объектов.

Самое приятное в NHibernate заключается в том, что он генерирует SQL на основе того, что вы указали в файле конфигурации. Исходя из этого, он поддерживает SQL Server, Oracle, MySQL, Firebird, PostGres и несколько других баз данных.

Ответ 4

Если вы должны сами его закодировать и не использовать продукт, который обеспечивает унифицированный доступ, помните, что такие объекты, как SqlDataAdapter и OracleDataAdapter, наследуются от общего DbDataAdapter (по крайней мере, в более поздних версиях среды выполнения). Если вы перейдете к DbDataAdapter, вы можете написать код, который будет работать с обеими базами данных в тех местах, где вы будете делать то же самое для обеих баз данных. Некоторые из вашего кода будут выглядеть примерно так:

DbDataAdapter adapter = GetOracleDataAdapter() as DbDataAdapter;

Как только вы отбросили, не имеет значения, является ли это SqlDataAdapter или OracleDataAdapter. Вы можете назвать это так же.

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

Ответ 5

Если вам требуется сопоставление записей базы данных с объектами, я предлагаю вам перейти к решению, которое уже было предложено: NHibernate. Если это кажется излишним для вашего приложения, и вы хотите использовать подход Ado.net и не нуждаетесь в O/RM-soultion, вам следует взглянуть на то, что сделали ребята Spring.net и узнали о Абстракция поставщика Ado.Net.

Ответ 6

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

Ответ 7

В таких случаях всегда хорошо создать многоуровневую архитектуру, где все связанные с БД вещи JUST находятся на уровне доступа к данным. Тогда у вас могут быть разные реализации вашего уровня DAO, один для Oracle, SQL Server и т.д.

Отделить бизнес-уровень от уровня DAO с помощью интерфейсов, чтобы ваш бизнес-уровень просто использовал их для доступа к уровню DAO. Таким образом, вы могли бы полностью обменяться подстилающей реализацией уровня DAO для работы в Oracle DB или любой другой системе, которая вам нравится.

Еще одно хорошее предложение - взглянуть на объектно-реляционные картографы, подобные Скотту. Я бы посмотрел на инфраструктуру NHibernate или Entity.

Ответ 8

Один из подходов к этой проблеме состоит в том, чтобы создать приложение для работы полностью с отключенными DataSets и написать компонент доступа к данным, который обрабатывает данные из разных брендов баз данных, которые вы будете поддерживать, а также сохраняющиеся изменения, внесенные в DataSets по вашему приложению обратно в исходные базы данных.

Плюсы: DataSets в .Net хорошо написаны, просты в использовании и мощны и отлично справляются с предоставлением методов и инструментов для работы с табличными данными.

Минусы: этот метод может быть проблематичным, если вашему приложению необходимо работать с чрезвычайно большими наборами данных на стороне клиента.

Ответ 9

В настоящее время Microsoft Entity Framework имеет несколько недостатков, некоторые из которых могут быть разрывами транзакций в зависимости от архитектуры приложения.

Из того, что я видел и читал о V2, который будет отправлен с .Net 4, я думаю, что он, безусловно, заслуживает того, чтобы его рассматривали.

Ответ 10

Многие люди предложили структуру отображения O/R, такую ​​как NHibernate. Это довольно разумный подход, если вы по какой-то причине не хотите использовать сопоставитель O/R. Что-то вроде NHibernate, вероятно, доставит вам 95% + пути, но вам может понадобиться написать какой-то пользовательский SQL. Не паникуйте, если это так; вы все равно можете сделать ad-hoc решение для остальных.

В этом случае возьмите биты, которые нуждаются в настраиваемом SQL, и разделите их на модуль плагинов, определенный платформой. Напишите плагины Oracle, MySQL, SQL Server (и т.д.), Необходимые для отдельных платформ баз данных, которые вы хотите поддерживать.

ADO.Net упрощает перенос sprocs, поэтому вы можете перенести уровень, зависящий от платформы, на некоторые хранимые процедуры, представляя более или менее согласованный API для среднего уровня. Есть еще некоторые зависимости от платформы (например, префикс @@для имен SQL Server), поэтому вам нужно будет создать общий механизм обертки sproc (что не так уж сложно).

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