2009-08-21 12 views
11

Projektujemy produkt, który może obsługiwać wiele baz danych. Robimy coś takiego obecnie tak, że nasz kod obsługuje MS SQL oraz MySQL:Jaki jest najlepszy sposób obsługi wielu baz danych dla produktu .NET?

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; 
      } 
     } 
    } 
} 

można zasugerować, czy istnieje lepszy sposób to zrobić? Jakie są plusy i minusy tego podejścia?

+2

Proponuję rozbić kod na różne bloki

, aby ułatwić nam czytanie.W rzeczywistości wiele kodu przepełnia prawy margines, a pod koniec kodu znajduje się tylko jeden poziomy pasek przewijania. –

Odpowiedz

2

dla bieżącej potrzeby, zgadzam się z NHibernate ...

prostu chcę podkreślić coś ze swojej hierarchii klas ...

będziesz lepiej w użyciu interfejs

odczuwalna (Wystarczy sprawdzić dokument lub Internet dla dokładnej składni)

Interface IDBParser 
    Function1 
    Function2 

class MSSQLParser : IDBParser 
    Function1 
    Function2 

class MySQLParser : IDBParser 
    Function1 
    Function2 

Następnie w kodzie można użyć interfejsu

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(); 

ten sposób będzie łatwiej zarządzać i Twój kod nie będzie obfitował w tym samym warunek if/innego. Znacznie łatwiej będzie dodać inne DB. Kolejną zaletą jest to, że może to pomóc w testowaniu jednostki, wysyłając fałszywy obiekt.

+0

Podobało mi się to podejście – Kalpak

1

Istnieją warstwy mapowania obiektowo-relacyjnego, które obsługują wiele technologii baz danych, takich jak Entity Spaces.

10

Cokolwiek robisz, nie napisać własny kod mapowania. Zrobiono to już wcześniej i prawdopodobnie zrobiono to milion razy lepiej niż wszystko, co można napisać ręcznie.

Bez wątpienia powinieneś używać NHibernate. Jest to obiektowo-relacyjny program odwzorowujący, który zapewnia przejrzystość dostępu do bazy danych: definiujesz zestaw klas DAL, które reprezentują każdą tabelę w bazie danych, i używasz dostawców NHibernate do wykonywania zapytań w bazie danych. NHibernate będzie dynamicznie generować SQL wymagany do wysłania zapytania do bazy danych i zapełnienia twoich obiektów DAL.

Fajną rzeczą w NHibernate jest to, że generuje SQL na podstawie tego, co zostało określone w pliku konfiguracyjnym. Po wyjęciu z pudełka obsługuje SQL Server, Oracle, MySQL, Firebird, PostGres i a few other databases.

+0

Nawet 1% wątpliwości? – MusiGenesis

+0

Od wątpliwości Juliet

0

Jednym ze sposobów rozwiązania tego problemu jest zaprojektowanie aplikacji do pracy całkowicie z odłączonymi zestawami danych i napisanie komponentu dostępu do danych, który obsługuje pobieranie danych z różnych marek baz danych, które będą obsługiwane, a także trwałe zmiany wprowadzone w produkcie. DataSets od aplikacji do oryginalnych baz danych.

Zalety: zestawy danych w .Net są dobrze napisane, łatwe w użyciu i wydajne, a także zapewniają doskonałą metodę dostarczania metod i narzędzi do pracy z danymi opartymi na tabelach.

Wady: Ta metoda może być problematyczna, jeśli aplikacja musi pracować z bardzo dużymi zbiorami danych po stronie klienta.

1

W takich przypadkach zawsze dobrze jest stworzyć architekturę warstwową, w której wszystkie elementy związane z bazą danych znajdują się TYLKO w warstwie dostępu do danych. Wtedy możesz mieć różne implementacje warstwy DAO, jedną dla Oracle, SQL Server itp.

Powinieneś oddzielić warstwę biznesową od warstwy DAO za pomocą interfejsów, tak, że twoja warstwa Biznesowa używa ich tylko do dostępu do DAO warstwa. Możesz więc idealnie wymienić implementację podkładania warstwy DAO, aby działała na bazie Oracle DB lub w dowolnym systemie, który ci się podoba.

Inną dobrą propozycją jest przyjrzenie się obiektowo-relacyjnym mapperom, takim jak Scott już zasugerował. Chciałbym rzucić okiem na strukturę NHibernate lub Entity.

0

W tej chwili Microsoft Entity Framework ma kilka skrótów, niektóre z nich mogą być wyłącznikami transakcji, w zależności od architektury aplikacji.

Z tego, co widziałem i czytałem na temat V2, który będzie dostarczany z .Net 4, myślę, że na pewno zasługuje na to, aby go obejrzeć.

0

Wiele osób zasugerowało strukturę mapowania O/R, na przykład NHibernate. Jest to dość rozsądne podejście, chyba że z jakiegoś powodu nie chcesz używać programu odwzorowującego O/R. Coś jak NHibernate prawdopodobnie dostaniesz 95% + drogi, ale możesz potrzebować napisać niestandardowy SQL. Nie panikuj, jeśli tak jest; nadal możesz zrobić rozwiązanie ad-hoc dla całej reszty.

W takim przypadku weź bity, które wymagają niestandardowego kodu SQL i podziel je na moduły wtyczek specyficzne dla platformy. Napisz wtyczki Oracle, MySQL, SQL Server (itp.) W razie potrzeby dla poszczególnych platform baz danych, które chcesz obsługiwać.

ADO.Net ułatwia pakowanie sproców, więc możesz przenieść warstwę zależną od platformy do niektórych procedur przechowywanych, prezentując bardziej lub mniej spójny interfejs API dla warstwy środkowej. Nadal istnieją pewne zależności między platformami (takie jak przedrostek "@" w nazwach zmiennych SQL Server), więc trzeba utworzyć ogólny mechanizm pakowania sproc (który nie jest wcale taki trudny).

Przy odrobinie szczęścia konkretne operacje, które trzeba wykonać w tym celu, będą wymagały niewielkiej liczby operacji, dlatego ilość pracy związanej z utrzymaniem wtyczek będzie ograniczona.

2

Jeśli konieczne jest samodzielne kodowanie, a nie używanie produktu zapewniającego ujednolicony dostęp, pamiętaj, że obiekty takie jak SqlDataAdapter i OracleDataAdapter dziedziczą ze wspólnego DbDataAdapter (przynajmniej w późniejszych wersjach środowiska wykonawczego). Jeśli rzucisz na DbDataAdapter, możesz napisać kod, który będzie działał z obu baz danych w miejscach, w których zrobiłbyś to samo dla obu baz danych. Niektóre kod będzie wyglądał trochę tak:

DbDataAdapter adapter = GetOracleDataAdapter() as DbDataAdapter; 

Po lanego w dół, to nie ma znaczenia, czy jest to SqlDataAdapter lub OracleDataAdapter. Możesz nazwać to tak samo.

Należy jednak pamiętać, że kodowanie dwóch baz danych oznacza używanie funkcji, które istnieją tylko w jednym i drugim, przy jednoczesnym wyeliminowaniu niedociągnięć obu. To nie jest dobry pomysł.

2

Jeśli potrzebujesz mapowania z wpisów w bazie danych do obiektów, sugeruję, aby przejść do rozwiązania, które już zasugerowano: NHibernate. Jeśli wydaje się to przesadą dla twojej aplikacji i chcesz iść z Ado.net i nie potrzebujesz O/RM-soultion, powinieneś rzucić okiem na to, co zrobili faceci z Spring.net i dowiedzieć się o Ado.Net Provider Abstraction.

Powiązane problemy