// The provider manifest token helps to distinguish between store versions. // We have only one backend version. // However, use the connection passed in to determine whether // the provider is local provider or remote provider // /// <summary> /// Returns provider manifest token for a given connection. /// </summary> /// <param name="connection">Connection to find manifest token from.</param> /// <returns>The provider manifest token for the specified connection.</returns> protected override string GetDbProviderManifestToken(DbConnection connection) { Check.NotNull(connection, "connection"); // vamshikb: Do we need to validate the connection and connection string // before returning the ProviderManifestToken???? // Determine the type of DbConnection // This method should never be called at runtime, so the provider // must be remote provider. // Throw if it is none. // if (connection.GetType() == typeof(SqlCeConnection)) { _isLocalProvider = true; } else if (RemoteProviderHelper.CompareObjectEqualsToType(connection, RemoteProvider.SqlCeConnection)) { _isLocalProvider = false; } else { throw ADP1.Argument(EntityRes.GetString(EntityRes.Mapping_Provider_WrongConnectionType, "SqlCeConnection")); } return(SqlCeProviderManifest.Token40); }
/// <summary> /// API for checkin whether database exists or not. /// This will internally only check whether the file that the connection points to exists or not. /// Note: In case of SQLCE, timeout and storeItemCollection parameters are ignored. /// </summary> /// <param name="connection"> Connection </param> /// <param name="timeOut"> Timeout for internal commands. </param> /// <param name="storeItemCollection"> Item Collection. </param> /// <returns> Bool indicating whether database exists or not. </returns> protected override bool DbDatabaseExists(DbConnection connection, int?timeOut, Lazy <StoreItemCollection> storeItemCollection) { Check.NotNull(connection, "connection"); Check.NotNull(storeItemCollection, "storeItemCollection"); // Validate and cast the connection. ValidateConnection(connection); if (_isLocalProvider) { return(CommonUtils.DatabaseExists(DbInterception.Dispatch.Connection.GetDataSource(connection, new DbInterceptionContext()))); } else { Type rdpType; // If we are working with RDP, then we will need to invoke the APIs through reflection. var engine = RemoteProviderHelper.GetRemoteSqlCeEngine( DbInterception.Dispatch.Connection.GetConnectionString(connection, new DbInterceptionContext()), out rdpType); Debug.Assert(engine != null); var mi = rdpType.GetMethod("FileExists", new[] { typeof(string), typeof(int?) }); Debug.Assert(mi != null); // We will pass 'timeout' to RDP, this will be used as timeout period for connecting and executing on TDSServer. return((bool)(mi.Invoke(engine, new object[] { DbInterception.Dispatch.Connection.GetDataSource(connection, new DbInterceptionContext()), timeOut }))); } }
// Private helper for validatingn the SqlCeConnection. private void ValidateConnection(DbConnection connection) { // Check whether it is a valid SqlCeConnection. var isValid = _isLocalProvider ? connection is SqlCeConnection : RemoteProviderHelper.CompareObjectEqualsToType(connection, RemoteProvider.SqlCeConnection); if (!isValid) { throw ADP1.InvalidConnectionType(); } }
/// <summary> /// API for deleting the database. /// In SQLCE case, this will translate to File.Delete() call. /// Note: Timeout and storeItemCollection parameters are ignored. /// </summary> /// <param name="connection"> Connection </param> /// <param name="timeOut"> Timeout for internal commands. </param> /// <param name="storeItemCollection"> Item Collection. </param> protected override void DbDeleteDatabase(DbConnection connection, int?timeOut, StoreItemCollection storeItemCollection) { Check.NotNull(connection, "connection"); Check.NotNull(storeItemCollection, "storeItemCollection"); // Validate that connection is a SqlCeConnection. ValidateConnection(connection); // We don't support create/delete database operations inside a transaction as they can't be rolled back. if (InTransactionScope()) { throw ADP1.DeleteDatabaseNotAllowedWithinTransaction(); } // Throw an exception if connection is open. // We should not close the connection because user could have result sets/data readers associated with this connection. // Thus, it is users responsiblity to close the connection before calling delete database. if (DbInterception.Dispatch.Connection.GetState(connection, new DbInterceptionContext()) == ConnectionState.Open) { throw ADP1.DeleteDatabaseWithOpenConnection(); } if (_isLocalProvider) { CommonUtils.DeleteDatabase(DbInterception.Dispatch.Connection.GetDataSource(connection, new DbInterceptionContext())); } else { try { Type rdpType; // If we are working with RDP, then we will need to invoke the APIs through reflection. var engine = RemoteProviderHelper.GetRemoteSqlCeEngine( DbInterception.Dispatch.Connection.GetConnectionString(connection, new DbInterceptionContext()), out rdpType); Debug.Assert(engine != null); // Invoke the required method on SqlCeEngine. var mi = rdpType.GetMethod("DeleteDatabaseWithError", new[] { typeof(string), typeof(int?) }); Debug.Assert(mi != null); // We will pass 'timeout' to RDP, this will be used as timeout period for connecting and executing on TDSServer. mi.Invoke(engine, new object[] { DbInterception.Dispatch.Connection.GetDataSource(connection, new DbInterceptionContext()), timeOut }); } catch (Exception e) { throw e.GetBaseException(); } } }
// <summary> // Constructs a SqlCeParameter // </summary> internal static DbParameter CreateSqlCeParameter( string name, TypeUsage type, object value, bool ignoreMaxLengthFacet, bool isLocalProvider) { var rdpSqlCeParameter = Type.GetType(RemoteProvider.SqlCeParameter); // No other parameter type is supported. // DebugCheck.NotNull(type); int? size; byte?precision; byte?scale; var result = isLocalProvider ? new SqlCeParameter() : (DbParameter)RemoteProviderHelper.CreateRemoteProviderType(RemoteProvider.SqlCeParameter); result.ParameterName = name; result.Value = value; // .Direction // parameter.Direction - take the default. we don't support output parameters. result.Direction = ParameterDirection.Input; // .Size, .Precision, .Scale and .SqlDbType var sqlDbType = GetSqlDbType(type, out size, out precision, out scale); // Skip guessing the parameter type (only for strings & blobs) if parameter size is not available // Instead, let QP take proper guess at execution time with available details. // if ((null != size) || (!TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.String) && !TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.Binary))) { if (isLocalProvider) { var sqlCeParameter = (SqlCeParameter)result; if (sqlCeParameter.SqlDbType != sqlDbType) { sqlCeParameter.SqlDbType = sqlDbType; } } else { // Remote Provider is loaded by reflection. As SqlDbType is not part of the base interface // We need to access this using reflection only. var rdpType = RemoteProviderHelper.GetRemoteProviderType(RemoteProvider.SqlCeParameter); var rdpInfo = rdpType.GetProperty("SqlDbType"); rdpInfo.SetValue(result, sqlDbType, null); } } // Note that we overwrite 'facet' parameters where either the value is different or // there is an output parameter. This is because output parameters in SqlClient have their // facets clobbered if they are implicitly set (e.g. if the Precision was implicitly set // by setting the value) if (!ignoreMaxLengthFacet && size.HasValue && (result.Size != size.Value)) { result.Size = size.Value; } if (precision.HasValue && (((IDbDataParameter)result).Precision != precision.Value)) { ((IDbDataParameter)result).Precision = precision.Value; } if (scale.HasValue && (((IDbDataParameter)result).Scale != scale.Value)) { ((IDbDataParameter)result).Scale = scale.Value; } // .IsNullable var isNullable = TypeSemantics.IsNullable(type); if (isNullable != result.IsNullable) { result.IsNullable = isNullable; } return(result); }
private DbCommand CreateCommand(DbProviderManifest providerManifest, DbCommandTree commandTree) { // DEVNOTE/CAUTION: This method could be called either from Remote or Local Provider. // Ensure that the code works well with the both provider types. // The methods called from the below code also need to be capable // of handling both provider types. // // NOTE: Remote Provider is loaded at runtime, if available. // This is done to remove hard dependency on Remote Provider and // it might not be present in all scenarios. All Remote Provider // type checks need to be done using RemoteProviderHelper class. // Check.NotNull(providerManifest, "providerManifest"); Check.NotNull(commandTree, "commandTree"); if (commandTree is DbFunctionCommandTree) { throw ADP1.NotSupported(EntityRes.GetString(EntityRes.StoredProceduresNotSupported)); } var command = _isLocalProvider ? new SqlCeMultiCommand() : (DbCommand)RemoteProviderHelper.CreateRemoteProviderType(RemoteProvider.SqlCeCommand); command.Connection = null; // don't hold on to the connection when we're going to cache this forever; List <DbParameter> parameters; CommandType commandType; var commandTexts = SqlGenerator.GenerateSql(commandTree, out parameters, out commandType, _isLocalProvider); if (_isLocalProvider) { Debug.Assert(command is SqlCeMultiCommand, "SqlCeMultiCommand expected"); // Set the multiple command texts for the command object ((SqlCeMultiCommand)command).CommandTexts = commandTexts; } else { // Set the command text for the RDP case. Debug.Assert(commandTexts.Length == 1, "BatchQueries are not supported in designer scenarios"); command.CommandText = commandTexts[0]; } command.CommandType = commandType; // Now make sure we populate the command's parameters from the CQT's parameters: // foreach (var queryParameter in commandTree.Parameters) { DbParameter parameter; const bool ignoreMaxLengthFacet = false; parameter = CreateSqlCeParameter( queryParameter.Key, queryParameter.Value, DBNull.Value, ignoreMaxLengthFacet, _isLocalProvider); command.Parameters.Add(parameter); } // Now add parameters added as part of SQL gen (note: this feature is only safe for DML SQL gen which // does not support user parameters, where there is no risk of name collision) // if (null != parameters && 0 < parameters.Count) { if (!(commandTree is DbDeleteCommandTree || commandTree is DbInsertCommandTree || commandTree is DbUpdateCommandTree)) { throw ADP1.InternalError(ADP1.InternalErrorCode.SqlGenParametersNotPermitted); } foreach (var parameter in parameters) { command.Parameters.Add(parameter); } } return(command); }
protected override void DbCreateDatabase(DbConnection connection, int?timeOut, StoreItemCollection storeItemCollection) { Check.NotNull(connection, "connection"); Check.NotNull(storeItemCollection, "storeItemCollection"); // Validate that connection is a SqlCeConnection. ValidateConnection(connection); // We don't support create/delete database operations inside a transaction as they can't be rolled back. if (InTransactionScope()) { throw ADP1.CreateDatabaseNotAllowedWithinTransaction(); } if (_isLocalProvider) { var engine = new SqlCeEngine(DbInterception.Dispatch.Connection.GetConnectionString(connection, new DbInterceptionContext())); engine.CreateDatabase(); engine.Dispose(); } else { try { Type rdpType; // If we are working with RDP, then we will need to invoke the APIs through reflection. var engine = RemoteProviderHelper.GetRemoteSqlCeEngine( DbInterception.Dispatch.Connection.GetConnectionString(connection, new DbInterceptionContext()), out rdpType); Debug.Assert(engine != null); // Invoke the required method on SqlCeEngine. var mi = rdpType.GetMethod("CreateDatabase", new[] { typeof(int?) }); Debug.Assert(mi != null); // We will pass 'timeout' to RDP, this will be used as timeout period for connecting and executing on TDSServer. mi.Invoke(engine, new object[] { timeOut }); } catch (Exception e) { throw e.GetBaseException(); } } // Create the command object depending on provider. var command = connection.CreateCommand(); // Create the command texts from StoreItemCollection. var commandTextCollection = SqlDdlBuilder.CreateObjectsScript(storeItemCollection, false); DbTransaction transaction = null; var interceptionContext = new DbInterceptionContext(); try { // Open the connection. DbInterception.Dispatch.Connection.Open(connection, interceptionContext); // Open a transaction and attach to the command. transaction = DbInterception.Dispatch.Connection.BeginTransaction(connection, new BeginTransactionInterceptionContext()); command.Transaction = transaction; // Execute each statement. foreach (var text in commandTextCollection) { command.CommandText = text; DbInterception.Dispatch.Command.NonQuery(command, new DbCommandInterceptionContext()); } // Commit the transaction. DbInterception.Dispatch.Transaction.Commit(transaction, interceptionContext); } catch (Exception e) { if (transaction != null) { // Rollback the transaction. DbInterception.Dispatch.Transaction.Rollback(transaction, interceptionContext); } // Throw IOE with SqlCeException embedded as inner exception. throw new InvalidOperationException(EntityRes.GetString(EntityRes.IncompleteDatabaseCreation), e); } finally { // Close connection and cleanup objects. if (command != null) { command.Dispose(); } if (transaction != null) { DbInterception.Dispatch.Transaction.Dispose(transaction, interceptionContext); } if (connection != null) { DbInterception.Dispatch.Connection.Close(connection, interceptionContext); } } }