using System; using System.ComponentModel; using System.Data.Common; using System.Data.Jet.JetStoreSchemaDefinition; using System.Data.OleDb; using System.Transactions; namespace System.Data.Jet { public class JetConnection : DbConnection, IDisposable, ICloneable { private ConnectionState _state; private string _ConnectionString; internal DbConnection InnerConnection { get; private set; } internal DbTransaction ActiveTransaction { get; set; } /// /// Initializes a new instance of the class. /// public JetConnection() { _state = ConnectionState.Closed; } /// /// Initializes a new instance of the class. /// /// The connection string. public JetConnection(string connectionString) : this() { this.ConnectionString = connectionString; } /// /// Gets or sets a value indicating whether this instance is empty. /// It is similar to connection to master and can be used only to create and drop databases /// /// /// true if this instance is empty; otherwise, false. /// public bool IsEmpty { get; set; } /// /// Gets the for this . /// protected override DbProviderFactory DbProviderFactory { get { return JetProviderFactory.Instance; } } /// /// Starts a database transaction. /// /// Specifies the isolation level for the transaction. /// /// An object representing the new transaction. /// protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) { if (State != ConnectionState.Open) throw new InvalidOperationException(Messages.CannotCallMethodInThisConnectionState("BeginDbTransaction", State)); if (ActiveTransaction != null) throw new InvalidOperationException(Messages.UnsupportedParallelTransactions()); switch (isolationLevel) { case IsolationLevel.Serializable: ActiveTransaction = new JetTransaction(InnerConnection.BeginTransaction(IsolationLevel.ReadCommitted), this); break; default: ActiveTransaction = new JetTransaction(InnerConnection.BeginTransaction(isolationLevel), this); break; } return ActiveTransaction; } /// /// Changes the current database for an open connection. /// /// Specifies the name of the database for the connection to use. public override void ChangeDatabase(string databaseName) { if (State != ConnectionState.Open) throw new InvalidOperationException(Messages.CannotCallMethodInThisConnectionState(nameof(ConnectionString), ConnectionState.Open, State)); throw new InvalidOperationException(Messages.MethodUnsupportedByJet("ChangeDatabase")); } /// /// Closes the connection to the database. This is the preferred method of closing any open connection. /// public override void Close() { if (_state == ConnectionState.Closed) return; if (ActiveTransaction != null) ActiveTransaction.Rollback(); ActiveTransaction = null; _state = ConnectionState.Closed; if (InnerConnection != null) { InnerConnection.StateChange -= WrappedConnection_StateChange; InnerConnectionFactory.Instance.CloseConnection(_ConnectionString, InnerConnection); } InnerConnection = null; OnStateChange(new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed)); } /// /// Gets or sets the string used to open the connection. /// public override string ConnectionString { get { return _ConnectionString; } set { if (State != ConnectionState.Closed) throw new InvalidOperationException(Messages.CannotChangePropertyValueInThisConnectionState(nameof(ConnectionString), State)); _ConnectionString = value; } } /// /// Gets the time to wait while establishing a connection before terminating the attempt and generating an error. /// For Jet this time is unlimited /// public override int ConnectionTimeout { get { return 0; } } /// /// Creates and returns a object associated with the current connection. /// /// /// A object. /// protected override DbCommand CreateDbCommand() { DbCommand command = JetProviderFactory.Instance.CreateCommand(); command.Connection = this; return command; } /// /// This property is always empty in Jet. Use DataSource property instead. /// Gets the name of the current database after a connection is opened, or the database name specified /// in the connection string before the connection is opened. /// public override string Database { get { return string.Empty; } } /// /// Gets the name of the file to open. /// public override string DataSource { get { OleDbConnectionStringBuilder connectionStringBuilder = new OleDbConnectionStringBuilder(_ConnectionString); return connectionStringBuilder.DataSource; } } /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { _ConnectionString = string.Empty; if (disposing) Close(); base.Dispose(disposing); } /// /// Enlists in the specified transaction. /// /// A reference to an existing in which to enlist. public override void EnlistTransaction(Transaction transaction) { if (InnerConnection == null) throw new InvalidOperationException(Messages.PropertyNotInitialized("Connection")); InnerConnection.EnlistTransaction(transaction); } /// /// Returns schema information for the data source of this using the specified string for the schema name. /// /// Specifies the name of the schema to return. /// /// A that contains schema information. /// /// /// /// public override DataTable GetSchema(string collectionName) { if (State != ConnectionState.Open) throw new InvalidOperationException(Messages.CannotCallMethodInThisConnectionState("GetSchema", State)); return InnerConnection.GetSchema(collectionName); } /// /// Returns schema information for the data source of this . /// /// /// A that contains schema information. /// /// /// /// public override DataTable GetSchema() { if (State != ConnectionState.Open) throw new InvalidOperationException(Messages.CannotCallMethodInThisConnectionState("GetSchema", State)); return InnerConnection.GetSchema(); } /// /// Returns schema information for the data source of this using the specified string for the schema name and the specified string array for the restriction values. /// /// Specifies the name of the schema to return. /// Specifies a set of restriction values for the requested schema. /// /// A that contains schema information. /// /// /// /// public override DataTable GetSchema(string collectionName, string[] restrictionValues) { if (State != ConnectionState.Open) throw new InvalidOperationException(Messages.CannotCallMethodInThisConnectionState("GetSchema", State)); return InnerConnection.GetSchema(collectionName, restrictionValues); } /// /// Opens a database connection with the settings specified by the . /// public override void Open() { if (IsEmpty) return; if (string.IsNullOrWhiteSpace(_ConnectionString)) throw new InvalidOperationException(Messages.PropertyNotInitialized(nameof(ConnectionString))); if (State != ConnectionState.Closed) throw new InvalidOperationException(Messages.CannotCallMethodInThisConnectionState(nameof(Open), ConnectionState.Closed, State)); _state = ConnectionState.Open; InnerConnection = InnerConnectionFactory.Instance.OpenConnection(_ConnectionString); InnerConnection.StateChange += WrappedConnection_StateChange; OnStateChange(new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open)); } /// /// Gets a string that represents the version of the server to which the object is connected. /// public override string ServerVersion { get { if (State != ConnectionState.Open) throw new InvalidOperationException(Messages.CannotReadPropertyValueInThisConnectionState(nameof(ServerVersion), State)); return InnerConnection.ServerVersion; } } /// /// Gets a string that describes the state of the connection. /// public override ConnectionState State { get { return _state; } } void WrappedConnection_StateChange(object sender, StateChangeEventArgs e) { OnStateChange(e); } public bool TableExists(string tableName) { ConnectionState oldConnectionState = State; bool tableExists; if (oldConnectionState == ConnectionState.Closed) Open(); try { string sqlFormat = "select count(*) from [{0}] where 1=2"; CreateCommand(String.Format(sqlFormat, tableName)).ExecuteNonQuery(); tableExists = true; } catch { tableExists = false; } if (oldConnectionState == ConnectionState.Closed) Close(); return tableExists; } public DbCommand CreateCommand(string commandText, int? commandTimeout = null) { if (String.IsNullOrEmpty(commandText)) // SqlCommand will complain if the command text is empty commandText = Environment.NewLine; var command = new JetCommand(commandText, this); if (commandTimeout.HasValue) command.CommandTimeout = commandTimeout.Value; return command; } /// /// Creates a new object that is a copy of the current instance. /// /// /// A new object that is a copy of this instance. /// object ICloneable.Clone() { JetConnection clone = new JetConnection(); if (InnerConnection != null) clone.InnerConnection = InnerConnectionFactory.Instance.OpenConnection(_ConnectionString); return clone; } /// /// Performs an explicit conversion from to . /// /// The connection. /// /// The result of the conversion. /// public static explicit operator OleDbConnection(JetConnection connection) { return (OleDbConnection)connection.InnerConnection; } /// /// Clears the pool. /// /// The connection. public static void ClearPool(JetConnection connection) { // Actually Jet does not support pools } /// /// Clears all pools. /// public static void ClearAllPools() { InnerConnectionFactory.Instance.ClearAllPools(); } public void CreateEmptyDatabase() { AdoxWrapper.CreateEmptyDatabase(_ConnectionString); } public static void CreateEmptyDatabase(string connectionString) { AdoxWrapper.CreateEmptyDatabase(connectionString); } public static string GetConnectionString(string fileName) { return $"Provider={JetConfiguration.OleDbDefaultProvider};Data Source={fileName}"; } public void DropDatabase(bool throwOnError = true) { DropDatabase(_ConnectionString, throwOnError); } public static void DropDatabase(string connectionString, bool throwOnError = true) { string fileName = JetStoreDatabaseHandling.ExtractFileNameFromConnectionString(connectionString); if (string.IsNullOrWhiteSpace(fileName)) throw new Exception("Cannot retrieve file name from connection string"); JetStoreDatabaseHandling.DeleteFile(fileName.Trim(), throwOnError); } public bool DatabaseExists() { return DatabaseExists(_ConnectionString); } public static bool DatabaseExists(string connectionString) { string fileName = JetStoreDatabaseHandling.ExtractFileNameFromConnectionString(connectionString); fileName = fileName.Trim('"'); if (string.IsNullOrWhiteSpace(fileName)) throw new Exception("Cannot retrieve file name from connection string"); return System.IO.File.Exists(fileName); } } }