// // Database.cs // // Copyright (c) 2011 Novell // // Authors: // Jérémie "garuma" Laval // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // using System; using System.Linq; using System.Threading; using System.Dynamic; using System.Data.Common; using System.Configuration; using System.ComponentModel; using System.Collections.Generic; namespace WebMatrix.Data { public class Database : IDisposable { public static event EventHandler ConnectionOpened; bool opened; DbConnection connection; readonly string providerName; static readonly Dictionary lastInsertedIdStmts = new Dictionary (); static Database () { // Add your own DB-specific way to do so lastInsertedIdStmts.Add ("System.Data.SqlClient", "select @@IDENTITY;"); lastInsertedIdStmts.Add ("Mono.Data.Sqlite", "select last_insert_rowid ();"); lastInsertedIdStmts.Add ("MySql.Data", "SELECT LAST_INSERT_ID();"); lastInsertedIdStmts.Add ("Npgsql", "SELECT lastval();"); } private Database (DbConnection connection, string providerName) { this.connection = connection; this.providerName = providerName; } public static Database Open (string name) { var config = ConfigurationManager.ConnectionStrings[name]; if (config == null) throw new ArgumentException ("name", string.Format ("Database with name {0} doesn't exist", name)); return OpenConnectionString (config.ConnectionString, config.ProviderName); } public static Database OpenConnectionString (string connectionString) { return OpenConnectionString (connectionString, "System.Data.SqlClient"); } public static Database OpenConnectionString (string connectionString, string providerName) { var factory = DbProviderFactories.GetFactory (providerName); var conn = factory.CreateConnection (); conn.ConnectionString = connectionString; return new Database (conn, providerName); } public void Close () { opened = false; connection.Close (); } public void Dispose () { Dispose (true); } protected virtual void Dispose (bool disposing) { if (disposing) { if (opened) connection.Close (); connection.Dispose (); } } public int Execute (string commandText, params object[] args) { var command = PrepareCommand (commandText); PrepareCommandParameters (command, args); EnsureConnectionOpened (); var result = command.ExecuteNonQuery (); command.Dispose (); return result; } public IEnumerable Query (string commandText, params object[] args) { var result = QueryInternal (commandText, args, false); return result != null ? result.Select (r => new DynamicRecord (r)) : null; } public dynamic QuerySingle (string commandText, params object[] args) { var result = QueryInternal (commandText, args, true); return result != null ? new DynamicRecord (result[0]) : null; } List> QueryInternal (string commandText, object[] args, bool unique) { EnsureConnectionOpened (); var command = PrepareCommand (commandText); PrepareCommandParameters (command, args); string[] columnsNames; var rows = new List> (); using (var reader = command.ExecuteReader ()) { if (!reader.Read () || !reader.HasRows) return null; columnsNames = new string [reader.FieldCount]; do { var fields = new Dictionary (); for (int i = 0; i < reader.FieldCount; ++i) { if (columnsNames[i] == null) columnsNames[i] = reader.GetName (i); fields[columnsNames[i]] = reader[i]; } rows.Add (fields); } while (!unique && reader.Read ()); } command.Dispose (); return rows; } public object QueryValue (string commandText, params object[] args) { EnsureConnectionOpened (); var command = PrepareCommand (commandText); PrepareCommandParameters (command, args); var result = command.ExecuteScalar (); command.Dispose (); return result; } public object GetLastInsertId () { string sql; if (!lastInsertedIdStmts.TryGetValue (providerName, out sql)) throw new NotSupportedException ("This operation is not available for your database"); return QueryValue (sql); } DbCommand PrepareCommand (string commandText) { var command = connection.CreateCommand (); command.CommandText = commandText; return command; } static void PrepareCommandParameters (DbCommand command, object[] args) { if (args.Length == 0) return; int index = 0; foreach (var arg in args) { var param = command.CreateParameter (); param.ParameterName = "@" + index; param.Value = args[index++]; command.Parameters.Add (param); } } static void TriggerConnectionOpened (Database self, DbConnection connection) { EventHandler evt = ConnectionOpened; if (evt != null) evt (self, new ConnectionEventArgs (connection)); } void EnsureConnectionOpened () { if (opened) return; connection.Open (); opened = true; TriggerConnectionOpened (this, connection); } public DbConnection Connection { get { return connection; } } } }