views:

667

answers:

6

I am currently developing a query builder application, basically a simple graphical interface that should allow users with no knowledge of SQL to define various queries on a database (joins, select, update, insert, delete). I will be using .Net 3.5. My application should support multiple databases, it should work with MS-SQL Server, MySQL and Oracle, so I would appreciate any hints or links to relevant lecture on how to design a provider independent DAL.

The user will select a database server, a database on the current server, provide the connection credentials, chose various tables, define queries (using a series of combo boxes) and finally execute the queries if they are valid. Of course, in the DAL I do want to have methods for each DB provider. I am thinking something on the lines of the factory pattern.

Note: This is a simple school project, so I am not interested in security or performance of the resulting queries.

UPDATE: After some more research and with the very valuable input that you have provided, I decided to use DbProviderFactory. ORM would be interesting but since I just want a query analyzer/builder I don't see the point of using one. So, I would appreciate if you would point me to a detailed tutorial on how to use DbProviderFactory and the associated classes.

A: 

I think ADO.NET Entity Framework (available since .NET 3.5 SP1) is a great choice as it pretty much abstracts away database-dependent SQL with its entity SQL language.

Mehrdad Afshari
As of now, ADO.NET EF supports Microsofts' DBMS only.
Anton Gogolev
Entity only works with MS SQL Server.
iulianchira
It's designed to be DB-independent at heart. I'm pretty sure there are EF providers for other DBMS as well (Google it). I'm not sure about stability of them as I haven't used them yet.
Mehrdad Afshari
Example: http://pgfoundry.org/frs/shownotes.php?release_id=1230
Mehrdad Afshari
A: 

You might be surprised, but a very simple provider independent DAL can be achieved with:

plain old DataSet and DataTable.

CLaRGe
A: 

I must say that editing a reasonably complex query visually is very cumbersome. And allowing users to insert/delete data using visual designer is a certain way to shoot yourself in the foot. A sized-down version of Management Studio, knowledge of basic SQL plus restricted server user will do a much better job.

If you're still inclined to design this app, you'll need NHibernate. More precisely, Criteria Queries will do the job, since they map pretty close to what you need.

Anton Gogolev
A: 

Most any ORM (Object-Relational Mapper) will know how to talk to a variety of database types.

As for allowing users to build their own queries: you need to be very careful with this. It's not so much that users could create malicious queries (though that can be a problem) as it is accident. It's surprisingly easy to write a query that will use all available server resources and create an effective denial of service for your database.

Joel Coehoorn
+2  A: 

I recommend using the System.Data.Common.DbProviderFactories class to generate generic ADO.NET classes.

As you find more .NET providers for databases you want to support, simply drop the provider DLL in the app's path and add a reference to the DbProviderFactory of the provider in the app.config file. You can have the user select the provider to use.

Here is an MSDN article on the topic: Obtaining a DbProviderFactory (ADO.NET)

I've used this approach before and been able to support MSSQL and SQLite in the same project with a minor configuration change.

Not sure if it'll work as well for a query builder app though...

Kevin Thiart
How in the world is this going to help him? Retrieving appropriate DbProviderFactory is miserable 0.2% of functionality, the most complex part will be actual query syntax, which is not covered by the DbProviderFactory functionality _at all_.
Anton Gogolev
He didn't ask anything about the query syntax. He asked about a provider-independent DAL. DbProviderFactory does give him an already-implemented factory pattern to base his DAL on. The lack of functionality is due to the fact that it has to be able to work with a wide range of RDBMS's and thus not use any of the vendor-specific features of the databases. If he sticks to straight selects, updates, deletes and inserts there shouldn't be any issues.But granted, ORM does give you more.
Kevin Thiart
As for using ORM: if the application has hard-coded business objects it'll work perfectly. But if the application is a simple query builder/runner with no specific domain (like MS's Query Analyzer), ORM is not much help as far as I know.Please correct me if I'm wrong. I'm not 100% clued up on all the uses of ORM.
Kevin Thiart
The "a simple graphical interface that should allow users with no knowledge of SQL to define various queries on a database" plus "...execute the queries if they are valid. Of course, in the DAL I do want to have methods for each DB provider" imply that query syntax is asked for.
Anton Gogolev
Ok, I see what you mean. Looks like I misunderstood the question.
Kevin Thiart
A: 

I'm not sure if this assists with your quest, but one thing I learned rather recently and took to heart is to have your data model's unique identifier implementation not propagate directly outside of the data layer, but to be wrapped in an abstraction. For instance, here is an interface which wraps a model's identifier:

public interface IModelIdentifier<T> where T : class 
{
    /// <summary>
    /// A string representation of the domain the model originated from.
    /// </summary>
    string Origin { get; }

    /// <summary>
    /// The model instance identifier for the model object that this 
    /// <see cref="IModelIdentifier{T}"/> refers to.  Typically, this 
    /// is a database key, file name, or some other unique identifier.
    /// <typeparam name="KeyDataType">The expected data type of the 
    /// identifier.</typeparam>
    /// </summary>
    KeyDataType GetKey<KeyDataType>();

    /// <summary>
    /// Performs an equality check on the two model identifiers and 
    /// returns <c>true</c> if they are equal; otherwise <c>false</c> 
    /// is returned.  All implementations must also override the equal operator.
    /// </summary>
    /// <param name="obj">The identifier to compare against.</param>
    /// <returns><c>true</c> if the identifiers are equal; otherwise 
    /// <c>false</c> is returned.</returns>
    bool Equals(IModelIdentifier<T> obj);
}

Your business logic layer, which may have in the past passed around ints as unique identifiers (for instance, from a identity column in your database table), is now passed as such:

    public IPerson RetrievePerson(IModelIdentifier<IPerson> personId)
    {
        /// Retrieval logic here...
    }

Your data layer will then have a class which implements IModelIdentifier<Person> and populates its internal data type with the physical model's unique identifier. This insulates your business layer from any changes you may have at the data layer, such as replacing your int key identifiers with Guids.

Jesse C. Slicer