16

We are making a fairly serious application that needs to remain agnostic to the DB a client wants to use. Initially we plan on supporting MySQL, Oracle & SQL Server. The tables & views are simple as are the queries (no real fancy SQL), therefore the question:

  1. Use native DB drivers (MySQLDbConnection etc.) and encapsulate the logic of executing queries and processing results or
  2. Use a generic OleDbConnection

Obviously option 2 involves no overhead, but I presuming the performance is not as great as with native access?

M4N
  • 94,805
  • 45
  • 217
  • 260
Andrew White
  • 1,770
  • 4
  • 25
  • 43
  • Just a point to mention if you are using ADO.NET: Microsoft are planning on deprecating their Oracle Data Provider (System.Data.OracleClient) [See here](http://msdn.microsoft.com/en-us/library/77d8yct7.aspx) – LauraB Jun 24 '10 at 15:52
  • Also see http://stackoverflow.com/questions/13133804/writing-driver-class-generic-for-any-database-support – nawfal Feb 20 '13 at 09:50

8 Answers8

14

Note: This answer is relevant if you decide to use basic ADO.NET 2 functionality instead of an ORM (such as Entity Framework or NHibernate) or LINQ to SQL.

Let's assume you've got a connection string defined in your app.config:

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

Notice the presence of the providerName attribute and its value. You could also put in a value for another DB provider, e.g. System.Data.SQLite.

(Note that non-standard providers, i.e. those that are not in the .NET Framework by default, need to be registered first, either in app.config or in the client machine's machine.config.)

Now, you can work with the specified database in a completely provider-agnostic fashion as follows:

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

Note how this code only works with interface types. The only place where you indicate a particular DB provider is through the providerName attribute value in the app.config file. (I've marked all the places where a setting from app.config is taken with ^^^s.)


Further reading:

stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
  • 3
    P.S.: What this answer does not cover is parameterised commands. .NET Framework doesn't provide a DB-provider-agnostic way for referring to command parameters in a SQL `CommandText`. – stakx - no longer contributing Oct 01 '11 at 16:43
  • I've had the following approach working with SQL Server and Oracle in the same application: DbParameter p = factory.CreateParameter(); p.ParameterName = "toDateTime"; p.DbType = DbType.Date; p.Value = toDateTime; command.Parameters.Add(p); – Thomas Bratt Jun 13 '12 at 10:46
  • 1
    @Thomas, you're correct, but then how will you refer to the parameter in the actual command (`IDbCommand.CommandText`)? Should it be `@toDateTime` (named parameter)? Or do you employ a `?` token (positional parameter)? Or some other syntax? ADO.NET doesn't really offer a provider-independent solution for this. – stakx - no longer contributing Jun 13 '12 at 18:54
  • Good point. When I've done this before (for Oracle, SQLite and SQL Server) I've done a string replace on the query text, converting ':' to '@' for SQL Server. It's not elegant but it works. Unfortunately, it gets unmanageable for other SQL syntax differences, so the SQL has to be kept at a lowest common denominator level. Best to use an ORM, obviously :) – Thomas Bratt Jun 14 '12 at 10:12
  • 2
    ... or question whether you *really* need your code to be DB-independent/agnostic. How often do you actually migrate a production DB to a different RDBMS? Perhaps not very often. – stakx - no longer contributing Jun 14 '12 at 11:40
7

IMHO using an ORM is a good design decision in order to have a database agnostic application. Switching database might be as easy as changing a config setting and connection string.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    Somewhat agree, but ORMs have their own issues, starting with performance. – Cylon Cat Jun 20 '10 at 20:22
  • 1
    @Cylon Cat, in large applications, as it seems is the case here, performance penalty incurred by ORM is really small compared to other parts and is outweigh by advantages such having a single language to express everything, maintainability, database agnosticism, filling the gap between the relational and object oriented world, .... – Darin Dimitrov Jun 20 '10 at 20:39
  • 1
    as a generalization, I agree, but as with all things, the performance penalty doesn't fall out evenly across an application. Some functionality may be particularly complex, or particularly performance-sensitive, and it helps to be aware of precisely how and where an ORM can run into problems. Prototypes and basic performance testing and profiling are very useful here. – Cylon Cat Jun 20 '10 at 21:03
5

You don't need OleDbConnection to access nonspecific ADO.NET providers. Just use DbConnection et. al. See DbProviderFactories on MSDN for more info.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
4

By including Oracle in that list, you've guaranteed that nothing will be simple.

  • Oracle uses a different prefix character (colon) for parameters, as compared to SQL Server that uses an "at" symbol.
  • Oracle uses a single data type (number) for long, int, short, boolean, float, and decimal; your code will have to be sure that you map these properly.
  • You must parameterize Oracle date and time values; if you try to use strings for dates in your SQL statements, you will go insane because of Oracle's date format. (Oracle uses a three-character month abbreviation; the format is 01-JAN-2010.)
  • Basic SQL functions for handling nulls can be different, particularly for null coalescing. ("NVL" versus "COALESCE") Oracle is much pickier about reserved words.
  • Oracle does not have native identity column support. Workarounds involve sequences, triggers, and requiring transactions just to retrieve an identity value from a new row.

In other words, your app can't be DB-agnostic. If you don't use an ORM, you will definitely want to build a data access layer that hides all these things from the rest of the application.

Voice of experience here. Just sayin'. For a common schema across SQL Server and Oracle, we've had to build most of the infrastructure of an ORM, while avoiding the aspects that can degrade performance. Interesting, but non-trivial, definitely!

Cylon Cat
  • 7,111
  • 2
  • 25
  • 33
  • 2
    Oracle doesn't use a three character month abbreviation. The date/time is help internally as seven bytes. When you convert to a character format it will a default format mask if you don't specify one. You can set that default format mask to whatever you want. – Gary Myers Jun 20 '10 at 22:43
  • 2
    PS Oracle supports COALESCE. The biggest difference between Oracle and other RDBMS vendors for NULLs is that Oracle doesn't supportzero length, non-null strings. PPS. Voted up,because I agree DB agnostic isn't easy – Gary Myers Jun 20 '10 at 22:45
  • @Gary, nice to know, thanks. That part of the migration had already been done when I got on the project. – Cylon Cat Jun 20 '10 at 22:54
2

LINQ is a highly regarded .NET ORM, partly because you can use it and stored procedures. Problem is, it's SQL Server only but people are working to provide similar functionality for Oracle & MySQL.

For database & query optimizations, I cringe at the idea of using an ORM. Data types, functions & overall syntax are not very portable in SQL. The most performant means of interacting with each database will be to tailor the model & queries to each one, but it means expertise, time and money. If need be, focus on one database vendor with the code setup to support vendor swap out & add support for other databases as necessary.

OMG Ponies
  • 325,700
  • 82
  • 523
  • 502
  • LINQ to Entities can support other databases. I *believe* it can support any ADO.NET data provider at runtime and the only limitations for non-SQLServer providers would be at design time. I'm working with it quite happily over SQLite; it only has a couple of design-time quirks. – Stephen Cleary Jun 21 '10 at 02:53
0

There's no good reason to avoid the most generic interfaces with the broadest support - OleDb and even ODBC if you're comfortable with them. Anything beyond that reduces the pool of products/languages/platforms/tools/developers you can work with. Being closest to the SQL metal, the vendor isn't going to introduce much inefficiency - certainly less than the more esoteric options. They've been around a long, long time to wring out any problems.

If you're going to add an abstraction layer (your own or someone else's), then that should be decided based on the merits of the abstractions introduced in your particular context, not just to have an abstraction layer (which is just more support unless there's an intentional benefit.)

As you can see, everyone's mileage varies. :) But in general, I think simpler is better.

dkretz
  • 37,399
  • 13
  • 80
  • 138
0

Why not use the Microsoft Patterns & Practices Enterprise Library Data Access Application Block. There's minimal overhead and switching providers is a snap.

Quote:

The Data Access Application Block takes advantage of these classes and provides a model that further supports encapsulation of database type—specific features, such as parameter discovery and type conversions. Because of this, applications can be ported from one database type to another without modifying the client code.

Laramie
  • 5,457
  • 2
  • 39
  • 46
0

You can always make part of the application database agnostic by having the bulk of the application use the DAL as a bunch of interfaces. The DAL itself would then provide a concrete implementation for the target database.

This way, you get decoupling in the use of the DAL, but the benefit of performance improvements or vendor specific constructs within the DAL.

Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187